Tag: T-SQL

  • The IF Statement in a T-SQL Query

    I’ve seen quite a few posts from people asking how to do something like this:

    SELECT 
      a.ID
    , IF a.MyChar = 'A' THEN 'Success'
      ELSE 'Fail'
    FROM MyTable a

    Of course, that doesn’t work in T-SQL, and you’ll get something like this:

    Msg 156, Level 15, State 1, Line 3

    Incorrect syntax near the keyword ‘IF’.

    Msg 156, Level 15, State 1, Line 3

    Incorrect syntax near the keyword ‘THEN’.

    There’s not IIF, no IF( x, then y, else z) construct. There is an IF … ELSE statement, but it’s use in code flows as a control statement such as

    DECLARE @i CHAR(1)
    SELECT @i = mychar FROM MyTable
    
    IF @i = 'A'
      SELECT 'Success'
    ELSE 
      SELECT 'Fail'
      

    Instead we have a CASE statement, which is designed to give you multiple choices. In the example above, I’d write:

    SELECT 
      a.ID
    , CASE WHEN a.MyChar = 'A' THEN 'Success'
      ELSE 'Fail'
      END
    FROM MyTable a

    I can even add multiple “WHEN” clauses if I want:

    SELECT 
      a.ID
    , CASE 
        WHEN a.MyChar = 'A' THEN 'Success'
        WHEN a.MyChar = 'B' THEN 'Close'
        WHEN a.MyChar = 'C' THEN 'Far'
        ELSE 'Fail'
      END
    FROM MyTable a

    Let your developers know that when they are looking for an inline IF type of logical statement, T-SQL gives them CASE instead.

  • The difference between REVOKE and DENY

    There was confusion recently with a poster that was moving permissions around and asked why I said they should revoke permissions and not deny them. I decided this was worth a post to explain.

    If I GRANT SELECT (or UPDATE/INSERT/DELETE) permissions to a user, then they can use those permissions to view data in a table. If I REVOKE the permissions, it’s the same as if the user never had them. They would need to be GRANTed permissions again to see the data.

    However, if I DENY them the ability to see data, then that’s different. They can’t see the data, but a subsequent GRANT will not allow them to see the data because the DENY will still be in effect.

    It’s a more permanent change, and should be used when you need to be sure that someone cannot see data, not when you are looking to remove permissions. To undo a GRANT, use REVOKE.

  • HASHBYTES – A T-SQL Function

    Someone was asking if the HASHBYTES function was a good one to use in T-SQL as far as performance goes.. I wish I had a good reference for the function, but the best one I had on SQLServerCentral was this piece on using it to load a data warehouse. I also wrote an editorial on it not working with strings beyond 8k, which seems to be a bug, or a lack of resources devoted to ensuring string functions work with varchar(max).

    The HASHBYTES function returns a hash of an input string. A hash is essential a calculation based on the values of the input, and two inputs that are the same, ought to produce the same hash. One catch with this function is that you provide the algorithm used, which can be one of these:

    • MD2
    • MD4
    • MD5
    • SHA
    • SHA1

    Each of these produces different output, returning a varbinary(max) value. As an example, suppose I hash “Steve Jones”

    SELECT HASHBYTES('MD2', 'Steve Jones') 'MD2'
    UNION
    SELECT HASHBYTES('MD4', 'Steve Jones') 'MD4'
    UNION
    SELECT HASHBYTES('MD5', 'Steve Jones') 'MD5'
    UNION
    SELECT HASHBYTES('SHA', 'Steve Jones') 'SHA'
    UNION
    SELECT HASHBYTES('SHA', 'Steve Jones') 'SHA1'
    
    

    The results look like this:

    MD2

    ———————————————-

    0x27851A666BFCB4A35F971DD742CDA15F

    0x2E978DE4841B1F3651A8DF4B2D2CF5F5C624A76B

    0x75931813C7EAAEAB3CD1D8D621935903

    0x979AC597C05CA6DE3A88C31A456D1125

    As you can see, there’s a different hash for the same value using different algorithms. However if I were to compare the same string to itself, I can easily tell if something has changed. If the hashes aren’t the same, there’s a difference. I’m not sure this is a great use, but the more obvious use is that I can hash a password and then have the user enter their own version, hash it, and compare the results. In this way, the system never needs to know the value.

    Just make sure you use the same algorithm Winking smile

  • Setting a Unique Index on a Bit Field

    Can you set a unique index on a bit field? Well, you can, but you’d end up with a very short table of two (or three) rows. I defined this table:

    CREATE TABLE [dbo].[BitTest](
        [MyBit] [bit] NULL,
        [MyName] [varchar](50) NULL
    ) ON [PRIMARY]
    
    GO
    
    USE [db1]
    GO
    
    CREATE UNIQUE NONCLUSTERED INDEX [IX_BitTest] ON [dbo].[BitTest] 
    (
        [MyBit] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    GO
    

    and then added some data

    INSERT BitTest SELECT 1, 'Test'
    INSERT BitTest SELECT 0, 'Test2'
    INSERT BitTest SELECT NULL, 'Test 3'
    
    SELECT * FROM BitTest

    This table has these rows:

    bittest2

    Adding another row:

    INSERT BitTest SELECT 1, 'Test 4'
    

    gets you an error:

    bittest3

    However what about a compound index? What if I make the table larger and add more fields. Here’s a larger table:

    CREATE TABLE UniqueBit
    ( BureauID INT
    , CompanyID INT
    , DivisionID int
    , DefaultType BIT
    , ProductName VARCHAR(100)
    )
    GO
    INSERT Uniquebit SELECT 1, 1, 1, 1, 'Product 1'
    INSERT Uniquebit SELECT 2, 1, 1, 1, 'Product 2'
    INSERT Uniquebit SELECT 3, 1, 1, 1, 'Product 3'
    INSERT Uniquebit SELECT 4, 1, 1, 1, 'Product 4'
    INSERT Uniquebit SELECT 1, 2, 2, 1, 'Product 5'
    INSERT Uniquebit SELECT 1, 2, 3, 1, 'Product 6'
    INSERT Uniquebit SELECT 4, 1, 1, 0, 'Product 7'

    If I now add a unique index:

    CREATE UNIQUE NONCLUSTERED INDEX [IX_UniqueBit] ON [dbo].[UniqueBit] 
    (
        [BureauID] ASC,
        [CompanyID] ASC,
        [DefaultType] ASC,
        [DivisionID] ASC
    ) ON [PRIMARY]
    GO
    

    It works fine. I can add another unique row like this:

    INSERT Uniquebit SELECT 4, 2, 1, 0, 'Product 7'

    without an error. Adding in a non-unique row:

    INSERT Uniquebit SELECT 4, 1, 1, 0, 'Product 7'
    

    gives me an error:

    bittest4

    There’s nothing special about a bit column for a unique index. There are restrictions for bit fields in some ways that relate to indexing, but uniqueness is not one of them.

    This was inspired by this post (before the complete details from the OP): http://www.sqlservercentral.com/Forums/Topic1126794-149-1.aspx#bm1126850