Tag: T-SQL

  • Transferring Table Types

    An interesting idea. I saw this question asked after I was playing with table types a bit. “Can you move a table type between schemas?”

    Suppose I had two schemas:

    CREATE SCHEMA OldSchema
    ;
    GO
    CREATE SCHEMA NewSchema
    ;
    GO

    In one of them, I create a table type and a procedure:

    CREATE TYPE OldSchema.MyTable AS TABLE
    ( IDCode INT
    , Location VARCHAR(200)
    )
    ;
    
    CREATE PROCEDURE OldSchema.MyProc 
    AS
     SELECT * FROM dbo.MyLogger
    ;
    

    There’s nothing fancy here. Just two objects created in one schema. I now have the need to move these to the other schema. Perhaps it’s a mistake. Perhaps I have developers working in one schema and I do integration testing in the other schema. In any case, it’s easy to move the proc with the ALTER SCHEMA syntax:

    ALTER SCHEMA NewSchema TRANSFER OldSchema.MyProc
    ;

    I can easily script something to move multiple procs, but if I do this:

    ALTER SCHEMA NewSchema TRANSFER OldSchema.MyTable
    ;

    I get this:

    Msg 15151, Level 16, State 1, Line 1

    Cannot find the object ‘MyTable’, because it does not exist or you do not have permission.

    I know it’s there; I just created it. What’s wrong?

    The problem is that this isn’t an object per se, but a type. As a result, to move a type, I need to use a different syntax:

    ALTER SCHEMA NewSchema TRANSFER type::OldSchema.MyTable
    ;
    GO

    That works fine and the type has moved. The class attribute of the notation is

    CLASS::Schema.Object

    I haven’t found good documentation of this, but there are numerous examples in BOL that show this is how you address various “types” in SQL Server.

  • Creating a User Defined Table Type

    I saw a post about a user defined table type in SQL Server and I was sure it was a typo. I kept thinking the poster meant table variable, but when I searched the term in Books Online, I was surprised to find User-Defined Table Types as an entry.

    These types are essentially templates that you can build for easier code reuse. They work in procedures and functions, or even as table variables. The CREATE TABLE syntax includes allowances for using these types.

    I can see this as being valuable when you have a structure that you want to pass into a module of some sort in multiple places and don’t want to have to include the code each time. I’m not sure it’s a great benefit, but it does prevent subtle mismatches like one module using varchar(50) for a column and another using varchar(200).

    A simple create for this type would be:

    CREATE TYPE StateTbl AS TABLE
    ( StateID INT
    , StateCode VARCHAR(2)
    , StateName VARCHAR(200)
    )
    ;
    

    This gives me a template I can use. Note that I can’t add rows to this table:

    INSERT StateTbl SELECT 1, 'CO', 'Colorado';
    

    I get this error:

    Msg 208, Level 16, State 1, Line 1

    Invalid object name ‘StateTbl’.

    It’s not an object yet. I need to instantiate an object based on this template. I can do that in a procedure:

    CREATE PROCEDURE SortStates
      @S StateTbl READONLY
     as
    
    SELECT StateName
     FROM @s
     ORDER BY StateName
    RETURN 0
    ;
    GO
    
    

    Fairly simple stuff. I can easily call this procedure, but I need a set of parameters first.

    DECLARE @p TABLE (id INT, scode VARCHAR(3), sname VARCHAR(20))
    
    INSERT @p
     VALUES (1, 'NC', 'North Carolina')
          , (2, 'VA', 'Virginia')
          , (3, 'CO', 'Colorado')
    ; 
    EXEC SortStates @p

    However this doesn’t work. The table isn’t compatible (I did that on purpose). Let’s clean it up.

    DECLARE @p TABLE StateTbl
       (StateID INT
       , StateCode VARCHAR(2)
       , StateName VARCHAR(200))
    
    INSERT @p
     VALUES (1, 'NC', 'North Carolina')
          , (2, 'VA', 'Virginia')
          , (3, 'CO', 'Colorado')
    ; 
    EXEC SortStates @p

    It still doesn’t work. There’s a binding here. I need to use the (cleaner) AS syntax for declaration.

    DECLARE @p as StateTbl
    
    INSERT @p
     VALUES (1, 'NC', 'North Carolina')
          , (2, 'VA', 'Virginia')
          , (3, 'CO', 'Colorado')
    ; 
    EXEC SortStates @p

    This returns results:

    udtt_1

    This means that you can use these types to create cleaner code, and enforce some standards (preventing things like people declaring columns with different lengths. However it also means that you have another “type” to manage and ensure everyone is using.

    I’m not sure how useful this is, but it is a neat little construct.

  • Finding DDL Triggers

    Triggers are the types of objects in SQL Server that are easy to lose track of. There isn’t an obvious way to tell that a table has a trigger on it and since most tables don’t have triggers, this is one of the things people often miss when troubleshooting unexpected results.

    DDL triggers are worse, since they aren’t tied to particular tables, but rather events. How can you find DDL triggers in your environment?

    There are a few ways. I’ll show you visually and in code.

    The GUI

    I like the Management Studio GUI to find information, and to quickly get code written. With SQL Prompt installed, I can get great intellisense that makes it easy to find parameters, names, objects, etc. I don’t like to run the actions from SSMS, but rather use the Script button and save the code, and execute it in a query window.

    In looking for server-side triggers, there is a “Server Objects” folder in the tree.

    ddl3

    Here is where you find your backup devices, endpoints, linked servers, and server level triggers. In this case, I can expand the folder (shown above) and find the trigger I created recently.

    At the database level, there’s a similar structure. Inside of a database, we find there is a programmability folder, which contains all the code items I can create in a database.

    ddl4

    In here we can see there is a Database Triggers item, and inside there are two triggers that I setup inside this database.

    You have to go look for these triggers, but if you’re wondering if they exist, you can find them here.

    Code

    The best way to look for triggers quickly is with code. Without resorting to BOL, I suspected there was some DMV that contained trigger code. As you can see below, I was right as typing SSF (a shortcut in Prompt), followed by “master.sys.server_t” got me this result:

    ddl5

    If I then examine the results from the server_triggers table, I get my one trigger at the server level.

    ddl6

    This is only part of the information needed as the server_trigger_events table has the events that will fire this trigger. I can query that to see I only have one event here:

    ddl7

    If I join in the events, then I can clean this up and get this:

    select
      t.name
    , t.object_id
    , t.is_disabled
    , te.type_desc
     FROM master.sys.server_triggers t
       INNER JOIN master.sys.server_trigger_events te
         ON t.object_id = te.object_id

    Which shows me the trigger, its ID, and the event’s.

    ddl8

  • Quick T-SQL Performance Comparison

    I’m not a T-SQL guru. When I have something that will run often, or I have performance concerns, I’ll ask someone like Jeff Moden or Wayne Sheffield to help me write a solution.

    However I have a few tricks to check things out quickly and determine what’s a better solution. Recently I ran across a thread asking for a solution to a problem that needed to sum data, but also pick values from a certain row. I posted a quick solution, and a few minutes later there were two others.

    I didn’t think mine was great, using a CTE and a subquery felt slightly inefficient, but was it really inefficient? I grabbed the third solution, which was similar to mine, and put both in SSMS. I then ran both pieces of code together, after clicking CTRL+M (include Actual Execution Plan).

    ; WITH MyCTE (acc_no, c_name, cnt)
    AS
    ( SELECT acc_no
           , c_name
           , COUNT(c_name)
       FROM #testing a
       GROUP BY acc_no
              , c_name  
    )
    SELECT 
      t.acc_no
    , c.c_name
    , number_sum = SUM( t.number) 
    , r_value_sum = SUM( t.R_Value) 
     FROM #TESTING t
       INNER JOIN mycte c
         ON t.acc_no = c.acc_no
     WHERE c.cnt = (SELECT MAX(d.cnt)
                     FROM MyCTE d
                     WHERE d.acc_no = c.acc_no
                   )
     GROUP BY t.acc_no
            , c.c_name
    ;
    
    with cte1 as (
    select acc_no,number,c_name,
           sum(R_Value) over(partition by acc_no) as R_Value,
           sum(time_spent) over(partition by acc_no) as time_spent,
           count(*) over(partition by acc_no,c_name) as cn
    from #TESTING),
    cte2 as (
    select acc_no,number,c_name,R_Value,time_spent,
           row_number() over(partition by acc_no order by cn desc,number desc) as rn
    from cte1)
    select acc_no,number,c_name,R_Value,time_spent
    from cte2
    where rn=1
    ;
    

    With all this code, I ran it and got this in the execution plan window (the results were the same and correct).

    comapretsql

    If you look at the top of each section, where it says “Query 1” and “Query 2”, and then look to the right, you’ll see the relative percentage of cost of the batch. With two queries in this batch, but solution was only slightly worse than the other solution (52% to 48%). That quickly tells me these are similar solutions.

    Now this isn’t an end-all, be-all way to look at queries. This is limited data, and unindexed tables. You’d want to test this with a few loads, and examine the details more closely if you are trying to tune these queries, but as a quick check, this helps to decide if you should think about abandoning one solution quickly.

    When I ran all three solutions (mine first, the 48% one above last), I got this:

    comapretsql2

    The second solution is much worse, almost twice as bad here, so I’d give that up and look at both of the other solutions in more detail if I wanted the optimum solution.

    And probably ask Jeff or Wayne for their opinion in the SSC forums. Winking smile