When I’ve created resources in Azure it’s usually taken from a few minutes and up to quarter of an hour – though sometimes longer.
When you’re new to this stuff, you can be uncertain and wonder, “Is it really creating it?”, “Did I hit the right buttons?”. As a result it can be handy to know where to check to see what’s going on.
Sometimes after creating the resource you are taken to a screen that will show you what’s going on:
And usually you can see something is occurring from the bar at the top:
If you click on the alarm icon you can see more details:
You can then click to see “More events in the activity log” to dig deeper:
This is all fairly intuitive, but earlier I was trying to create a SQL Database Managed Instance for the first time. It showed some activity in the items above for a few minutes, but after that nothing happened. Had it failed? Had I done something wrong? Should I start again and try to create a new one?
The answer was to select resource groups from the blades on the left, and select the resource group that I had created the item in:
On the right hand side I can see an item saying “Deployments” and I can see that one is in the process of deploying. I can click the hyperlink for more details:
The third item in the list was the one I was looking for:
Okay, so it is in the process of being created. There’s no way to tell how long it will take but at least I now know it’s happening.
While searching for it I did notice a warning on the create screen for the resource that I hadn’t seen when I first whizzed through the creation:
The SEQUENCE object was added to T-SQL in SQL Server 2012. It’s reasonably well known to DBAs, but less so to developers or those new to SQL, so I thought I’d produce a quick post to demonstrate its use.
In basic terms, a SEQUENCE is a way of generating a sequence of numerical values. The following are examples of sequences you could generate:
1, 2, 3, 4, 5 6, 7, 8, 9…
1, 6 , 11, 16, 21, 26, 31…
1000, 1001, 1002, 1003, 1004…
You can pick a starting number (and an ending number if you want), a data type (which might produce a natural maximum to the size of the sequence) and an increment (i.e. how much you want to be added to produce the next number in the sequence). There are other options, but I’m going to focus on the simplest use case. You can find the full documentation here:
So, let’s define my use case. I have a table to hold customer orders. For each record want to define an Order Reference Number of the format ORN0000000001.
Now you could implement something using an IDENTITY column to manage this – but there may be times when that is not ideal, for instance your table may not already have a suitable identity to use (you might have a unique identifier as the primary key) and if you want to store the actual reference then you’d need to add an IDENTITY column in addition to the reference column. Or you might need a reference that is unique across multiple tables.
The SEQUENCE object is also designed to be faster than IDENTITY, creating less blocking when you have a lot of concurrent inserts.
First of all, creating the sequence to generate the numeric part of my reference is easy. Let’s say that a bunch of reference numbers have already been used so I want to start with ORN0000100001
Let’s look at the SQL…
CREATE SEQUENCE dbo.OrderRefSequence AS bigint START WITH 100001 INCREMENT BY 1;
Then I can request numbers from the sequence using NEXT VALUE FOR e.g.
SELECT NEXT VALUE FOR dbo.OrderRefSequence;
The first time I run that I get the starting number 100,001.
Another nice addition to SQL Server 2012 was the FORMAT function which we can use to format the number into a string whilst padding it with leading zeroes and adding the text prefix:
SELECT FORMAT(NEXT VALUE FOR dbo.OrderRefSequence, 'ORN0000000000#');
That returns me ORN00000100002.
If I keep executing it then the reference increases:
So, now I can just use that when inserting values to my table to get a new reference number each time.
But, what’s even nicer is that you can do it all by defining a default for your column and referencing the sequence in the default.
I’ll create the following table:
CREATE TABLE dbo.Orders ( Id UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWSEQUENTIALID(), CustomerId UNIQUEIDENTIFIER NOT NULL, OrderReference VARCHAR(20) DEFAULT(FORMAT(NEXT VALUE FOR dbo.OrderRefSequence, 'ORN0000000000#')), OrderDate DATETIME DEFAULT(GETUTCDATE()));
You can see that the OrderReference is defined with a default using our sequence object.
I insert a bunch of rows into the table. For the sake of this rather contrived example, I only need to specify the CustomerId. I do that by generating a bunch of random unique identifiers – one for each row in the sys.objects table.
INSERT INTO dbo.Orders (CustomerId) SELECT NEWID() FROM sys.objects;
Let’s have a look at an extract from the table:
You can see I’ve got a nice series of ascending, non-duplicating reference numbers.
One thing to note is that, while the sequence will generally produce unique number, it is still worth enforcing that in your table definition with a unique constraint i.e.
ALTER TABLE dbo.Orders ADD CONSTRAINT UQ_Orders_OrderReference UNIQUE(OrderReference);
This prevents someone from issuing an UPDATE command that might create a duplicate reference. Also, once the sequence runs out of numbers it will repeat back at the beginning unless you specify NO CYCLE in the defintion of the sequence – obviously in most applications this is unlikely to be an issue if you’re using a bigint for the sequence.
There was also a bug in some versions of SQL 2012 and 2014 that meant a duplicate could get created when your server was under memory pressure:
This was fixed with SQL Server 2012 SP2 CU4 and SQL Server 2014 CU6 – but it’s better to be safe than sorry.
As a final note, it’s worth remembering that with the GDPR, these sorts of references are defined as personal data.That’s one good reason not to ever consider using these sorts of references as the primary key of your table (there are many others) – but also a reason why – where you already have an identity based primary keys that you could use to generate the references – it may be worth decoupling them from the primary key and basing them on a separate sequence instead.
Like Andy, I didn’t take a straight route into being a DBA. I actually trained as a teacher, and did a variety of temp jobs – largely in call centres – before I got my first “proper” job as a developer.
My first steps into development were when I was a team leader in a call centre. There was a lot of paper shuffling going on. I felt there had to be a better way of doing things, so developed processes using Access and Excel and convinced the Admin team in the benefits of using them.
Not exactly development, more Macros with a bit of VBA, but that got me spotted by another team that were trying to automate reporting in the call centre – again using MS Access. That was where I first encountered relational databases and got deeper into coding. I joined as they were in the process of taking an Excel spreadsheet that processed flat files using macros – taking the whole day/every day to chunder away, replacing it with a coded solution. Finally we had it importing all the call center stats in seconds on a schedule in the early hours.
Nowadays that seems trivial, but back then it felt like a great achievement. It’s still one of my best work memories – when I showed my boss the automated import for the first time.
Moving forward from there and trying to answer Andy’s question, I realised that what started me off is what’s kept moving me forward. That there’s always got to be a better way.
Another of my favourite early developer memories was being asked to look at whether a file import to an application could be sped up as it was taking 10 minutes. The process was to upload a file with corporate credit card statements from a flat file into a SQL database for the application that companies used to manage them. When I opened it up I was quite impressed with the code, it seemed cleverer than anything I could have written – but maybe a bit too clever. I asked myself if there was a better and simpler way of achieving the same thing. There was, and it turned out to be a lot lot quicker.
Making things run faster remained something I loved doing, but it often seemed often that optimisation wasn’t a big focus in application coding – that was more about delivering features. I think that’s part of what led me to specialise in data where performance, particularly at scale, is so key. Looking at SQL code and going – “well, sure it works as it is, but is there not a better way of doing this.”
It strikes me that for a lot of us, this is how we ended up in software, we saw things being done a certain way and saw scope for improvement. Felt that there must be a better way.
And it’s good to remember this isn’t an attitude where we can rest on our laurels. Even when we know what we’re doing, have done something a hundred times before, it’s still good to say “There’s got to be a better way”, and to think about what that might be. Not only does that mean we’re continually improving, but it’s also what keeps working in technology fresh and interesting.
I’ve recently encountered an issue that was difficult to resolve and I didn’t find the particular cause that was troubling us documented elsewhere on the web so thought I’d record it here.
The issue was with a service account connecting to SQL Server and intermittently failing to logon.
Errors reported in the Windows Application Event log were:
SSPI handshake failed with error code 0x8009030c
Login failed. The login is from an untrusted domain and cannot be used with Windows authentication.
The login attempt didn’t appear to get as far as the SQL instance, so no further information could be captured in a failed Logins trace.
This was affecting a large number of application servers using the same service account. Fortunately this was in development and test environments so no production impact.
The problem was that the account was getting locked out. A service was running every half hour using the account to connect to SQL, but with the wrong password. We also had a process running to unlock locked service accounts – so the account would start working again after a few minutes.
The resolution was to kill that service as it was no longer required. We were able to identify where the failed logins were coming from via the Active Directory audit logs for the account in question.
This was particularly difficult to troubleshoot as the error was a bit misleading.