Tag: T-SQL

  • Using AI to Test a SQL Query

    Someone sent me this code.

    WITH p AS
     ( SELECT ID, ROW_NUMBER() OVER (ORDER BY ID ASC) AS RN
     FROM wp_posts
     WHERE post_parent = 94341; )
     UPDATE p SET menu_order = RN

    That’s not great code. In fact, it won’t run. I know that, but does an AI? Let’s see how Copilot does.

    This is part of a series of experiments with AI systems.

    The First Test

    I entered this prompt, but accidentally hit Enter. When I then pasted in the code and hit enter, I got this:

    2025-05_line0012

    That is just about what I would have sent the author to let them know this code doesn’t work. It’s good to see a GenAI LLM can recognize this.

    I then pasted in the entire prompt which was: “give me a table with the schema to match this code: WITH p AS ( SELECT ID, ROW_NUMBER() OVER (ORDER BY ID ASC) AS RN FROM wp_posts WHERE post_parent = 94341; ) UPDATE p SET menu_order = RN”

    The Gen AI response was polite, with this answer:

    Certainly! Based on your SQL code, it looks like you need a table schema that matches the structure of wp_posts, particularly including ID, post_parent, and menu_order. Here’s a possible schema:

    I then had this code, with a copy button that I could copy/paste into SSMS.

    CREATE TABLE wp_posts (
         ID INT PRIMARY KEY,      -- Unique identifier for each post
         post_parent INT,         -- Parent post ID (for hierarchical structures)
         menu_order INT           -- Order value for arranging posts
    );

    That’s what I would have done to mock this up for testing. You might guess the next step: data.

    Here’s my prompt: create some test data for the table with Ids that increment but are not necessarily sequential and post_parent values that match the ID values

    That got my this response.

    2025-05_line0016

    That’s not bad, but it’s a limited set of data. Let’s ask for more.

    2025-05_line0031

    Below this the GenAI LLM gave me a summary that explains what it did.

    2025-05_line0032

    Next, I wanted a test. I used this prompt: write a tsqlt test to verify that the update statement works to reset the menu_order to sequential values for a particular post_parent

    I got a nice response and the code I’m showing below the image.

    2025-05_line0033

    Here is the code:

    -- Step 1: Create a test class
    EXEC tSQLt.NewTestClass 'TestMenuOrderUpdate';

    GO

     

    — Step 2: Define the test
    CREATE PROCEDURE TestMenuOrderUpdate.[test_UpdateMenuOrder_Sequential]
    AS
    BEGIN
    — Arrange: Fake the wp_posts table
    EXEC tSQLt.FakeTable ‘dbo.wp_posts’;

     

    INSERT INTO wp_posts (ID, post_parent, menu_order)
    VALUES
    (101, 94341, 5), (102, 94341, 3), (103, 94341, 8),
    (104, 94341, 1), (105, 94341, 7);

     

    — Act: Run the update logic
    WITH p AS (
    SELECT ID, ROW_NUMBER() OVER (ORDER BY ID ASC) AS RN
    FROM wp_posts
    WHERE post_parent = 94341
    )
    UPDATE wp_posts
    SET menu_order = p.RN
    FROM p
    WHERE wp_posts.ID = p.ID;

     

    — Assert: Check if menu_order is sequential
    EXEC tSQLt.AssertEqualsTable ‘ExpectedResults’, ‘wp_posts’;

     

    END;
    GO

     

    — Step 3: Create expected results table for assertion
    CREATE TABLE ExpectedResults (
    ID INT PRIMARY KEY,
    post_parent INT,
    menu_order INT
    );

     

    INSERT INTO ExpectedResults (ID, post_parent, menu_order)
    VALUES
    (101, 94341, 1), (102, 94341, 2), (103, 94341, 3),
    (104, 94341, 4), (105, 94341, 5);

     

    — Run the test
    EXEC tSQLt.Run ‘TestMenuOrderUpdate’;

    
    

    This is where I stopped. This code runs, though it’s brittle in that the Expected Results table is a real table and not a temp inside the test. I can easily edit that and likely quicker than the GenAI can. It’s copy/pasting some code for me from one spot to another. I also need to add a CREATE OR ALTER to the proc.

    Summary

    A quick use of an AI that easily saved me 5-10 minutes to mock of a scenario and show someone what they were doing wrong and also testing their code. I mock up things regularly to test them, and often do something like this to answer questions. This is a good place where AI can save some time, and might be worth using. I’m also glad to see it recognize and write some decent SQL code for me, especially the insert statement for test data.

  • Create a Linked Server: #SQLNewBlogger

    I had a customer recently that was asking about Linked Servers and some development advice. I was going to show them a few things and realized I hadn’t created a linked server in my demo environment, so I did it and decided to create a quick post on this.

    Another post for me that is simple and hopefully serves as an example for people trying to get blogging as #SQLNewBloggers.

    The Scenario

    I have a few demo instances of SQL Server in my local environment: Aristotle and Aristotle\SQL2022. In this case I was connected to the named instance, and decided to create a connection to Aristotle. As you can see, I don’t have any linked servers in the named instance.

    2025-04_0125

    To create a linked server, I can use this simple code:

    EXEC master.dbo.sp_addlinkedserver   
         @server = N'Aristotle',   
         @srvproduct=N'SQL Server';  
    GO

    This creates the linked server (as you can see below), with a number of defaults. In this case, the security is made with whatever login queries the linked server.

    2025-04_0126

    You can see the security properties here:

    2025-04_0127

    This might be OK in your enviroment, or it might not be. Perhaps you need to ensure everyone querying the remote server uses the same login. In which case, the sp_addlinkedserver procedure doesn’t do this. You would need to use sp_addlinkedsrvlogin to do that. That’s for another post.

    NOTE: Be sure you understand what a linked server does, how to use it, and the downsides. There are many and this can slow down your application or overload servers

    I can test this connection by right clicking the Linked Server in SSMS:

    2025-04_0128

    This works, as expected.

    2025-04_0129

    I can also run a query through the linked server, using 4-part naming with the linked server, then the database, schema, and table. This also works:

    2025-04_0160

    That’s it to get started. I recommend you be careful when using linked servers as this creates a bit of a tight coupling and makes development harder. I might recommend you get away from querying database server to database server when possible and let an application do this work if it’s possible.

    SQL New Blogger

    Linked Servers aren’t that common, but they aren’t rare. This is a skill that SQL Server people should have and understand a bit about. This post is very basic, but it provides a jumping off point where I could write a number of other posts related to linked servers and perhaps guide an interviewer along a path of asking me about them. I certainly showcase some knowledge here if someone asks me if I’ve ever created one.

    This post took me about 10 minutes to test and write, and you could probably do this in your environment. You don’t even need to servers, as you could create a loopback linked server.

  • Can I Change a Primary Key Value? #SQLNewBlogger

    I heard someone say recently that you can’t change a primary key value in a row. That’s not the case, so I decided to show a quick proof of that.

    Another post for me that is simple and hopefully serves as an example for people trying to get blogging as #SQLNewBloggers.

    The Scenario

    Let’s set up a simple table with some data.

    CREATE TABLE PKChangeTest (
    ImportantNumber VARCHAR(20) NOT NULL CONSTRAINT PKChangeTestPK PRIMARY KEY
    , CustomerName VARCHAR(50)
    , StatusValue INT)
    GO
    INSERT dbo.PKChangeTest
       (ImportantNumber, CustomerName, StatusValue)
    VALUES
       ('1234567', 'Steve', 1)
    ,  ('2345678', 'Andy', 1)
    ,  ('3456789', 'Brian', 1)
    ,  ('1235667', 'Leon', 1)
    ,  ('1265567', 'Dave', 1)
    ,  ('9914567', 'Bill', 1)
    GO

    If I look at this table, I have some unique numbers making up the PKs. If I select from the table, I can see the data.

    2025-04_0117

    Now, let’s change some data. I’ll change the PK values with a few statements. Then I’ll select from the table, and we will see things changed.

    2025-04_0118

    The ImportantNumber for both Bill and Steve have changed. These are PK modifications.

    We can change a PK value. These are not set in stone once inserted.

    SQL New Blogger

    This is a short look at something that’s a myth among some people. When I heard someone say this, I knew I needed to prove this. The scenario took just about 5 minutes to set up (even without AI), and then it was another 10 minutes to structure and write this post.  I actually have 2 more ideas from this on things I can show to prove how PKs work and are malleable.

    You can do the same thing. When you wonder about something, or hear something that isn’t true from others, prove it. And blog about it.

  • Limiting Results with TEXTSIZE in SQL Server: #SQLNewBlogger

    There is a SET command in SQL Server that changes how much data is returned from some fields. This short post shows what I learned about the SET TEXTSIZE command.

    Another post for me that is simple and hopefully serves as an example for people trying to get blogging as #SQLNewBloggers.

    The Scenario

    Let’s start with a little code. I actually created a table that looks like this:

    CREATE TABLE [dbo].[Beer]
    (
    [BeerID] [int] NOT NULL IDENTITY(1, 1),
    [BeerName] [varchar] (20) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [brewer] [varchar] (20) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [beerdescription] [varchar] (max) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    GO

    I added data so that I have something in here:

    INSERT INTO dbo.Beer
       (BeerName, brewer, beerdescription)
    VALUES
      (1,    'Becks', 'Interbrew', 'Beck''s is a German-style pilsner beer known for its golden color, full-bodied taste, and a crisp, clean finish with floral and fruity hop aromas, brewed according to the German purity law')
    ,(2,    'Fat Tire', 'New Belgium    Toasty malt, gentle sweetness, flash of fresh hop bitterness. The malt and hops are perfectly balanced.')
    ,(3,    'Mac n Jacks', 'Mac & Jack''s Brewery', 'This beer erupts with a floral, hoppy taste, followed by a well rounded malty middle, finishing with a nicely organic hop flavor. Locally sourced two row grain and a blend of specialty malts give our amber its rich taste.')
    ,(4,    'Alaskan Amber', 'Alaskan Brewing', 'Alaskan Brewing Amber Ale is an "alt" style beer, meaning it''s fermented slowly and at colder temperatures, resulting in a well-balanced, richly malty, and long-lasting flavor profile with a clean, pleasing aftertaste.')
    ,(8,    'Kirin', 'Kirin Brewing', 'Kirin Ichiban is a Lager-type beer, which means it is fermented at low temperatures and offers a light and refreshing texture with a smooth and balanced flavor.')

    Now, let’s see what this setting does.

    SET TEXTSIZE

    This command changes the behavior of SELECT queries and controls how much data is returned in bytes. The setting is the command with an integer after it. The max value is 2GB.

    I’ll run a normal query, then I’ll set a smaller size and repeat the query. Notice how the results differ below. I get less data in the second query.

    2025-04_0218

    What this setting does is limit the number of bytes from some fields. I only have 20 characters from each description.

    Let’s do one more query. I’ll lower the value to 5.

    2025-04_0219

    Note that while the description is very low, the name and brewer are not cut off.

    The explanation is that this works on the max types: varchar(max), nvarchar(max), varvinary(max), text, ntext, and image. Non-max fields aren’t affected.

    Also note that the default setting from the SQL Native Client and ODBC driver is –1, for unlimited data. That explains why I haven’t noticed this as I’m often using an app that uses one of those. IF you set this to 0, then it defaults to 4KB.

    A nice way to prevent apps from grabbing tons of data unless they need it, though you’d certainly need to help users understand why they weren’t getting all the data expected. I think long fields (or image/audio/etc. data) would need a “get more” or “get all” item in software to reset this and return the full value in the table.

    SQL New Blogger

    This was a function I ran across, whose purpose I had no idea about. I read it, experimented, and in about 30 minutes had put together this demo and post. Easy to do and quick.

    I learned something, and I’m sharing this with potential employers. You could as well, with a little effort.