代码之家  ›  专栏  ›  技术社区  ›  Eoin Campbell

SQL Server,where子句与不同类型和默认转换行为的比较

  •  5
  • Eoin Campbell  · 技术社区  · 17 年前

    正当这件事让我今天困惑了一段时间,所以也许你们中的一位SQL Server bright sparks可以为这种行为提供一些线索。

    我们有一张桌子 Phones . 在其中,电话号码存储为nvarchars格式,其中包含国际格式的号码,仅为数字格式。。。那么是一个美国号码 +1-(212)-999-9999 存储为 12129999999

    IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Phones')
    BEGIN
        DROP TABLE Phones
    END
    GO
    
    CREATE TABLE [dbo].[Phones]
    (
        [ID] [int] IDENTITY(1,1) NOT NULL,
        [Mobile] [nvarchar](50) NOT NULL,
        CONSTRAINT [PK_Phones] PRIMARY KEY CLUSTERED 
        ( [ID] ASC )
        WITH (
          PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, 
          IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
          ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    
    DECLARE @biMobile_1 bigint
    DECLARE @biMobile_2 bigint
    SET @biMobile_1 = 12121111111
    SET @biMobile_2 = 12129999999
    
    Print 'Inserting Phone Number'
    INSERT INTO Phones (Mobile) VALUES ('12121111111')
    
    Print 'Selecting Phone Number'
    SELECT * FROM Phones WHERE Mobile = @biMobile_1 --Select #1
    
    Print 'Inserting Junk Data'
    INSERT INTO Phones (Mobile) VALUES ('JUNK DATA')
    INSERT INTO Phones (Mobile) VALUES ('12129999999')
    
    Print 'Selecting From Table Containing Junk'
    SELECT * FROM Phones WHERE Mobile = @biMobile_1 -- Select #2
    SELECT * FROM Phones WHERE Mobile = @biMobile_2 -- Select #3
    

    第一个选择(标记为#1)将起作用 第二个选择(标记为#2)将起作用,但随后会立即出现错误 第三个select(标记为#3)不返回任何内容。

    Error converting data type nvarchar to bigint.
    

    现在,这似乎完全是疯狂的行为。我以为会发生的是

    1. SQL实现了在数据库中比较两种不同数据类型的功能 WHERE
    2. 它将尝试将@variable转换为列的数据类型
    3. 如果失败了,抛出一个错误,它就成功了,太棒了!!!

    实际上似乎正在发生的是

    1. SQL实现了在数据库中比较两种不同数据类型的功能 条款
    2. 它逐行将列中的值转换为@变量的数据类型
    3. 对于每个成功的转换,它都会进行比较,并返回该行。
    4. 如果它在列中找到一个无法转换的值,它就会爆炸,返回到目前为止找到的任何数据,并且不会继续遍历表。

    笔记我在SQL2005中做了这个测试,但它在SQL2K中也是可复制的。

    6 回复  |  直到 17 年前
        1
  •  4
  •   ahains    17 年前

    数据类型优先级定义良好- http://msdn.microsoft.com/en-us/library/ms190309.aspx

    编辑-澄清一下,sql并非总是将列类型转换为参数类型。它只是遵循我给出的链接中的类型优先级。这可能意味着参数被转换为列类型,如果类型优先级要求这样做的话。

        2
  •  1
  •   ChrisLively    17 年前

    我看不出有什么问题。为什么SQL server知道1000条记录中的232条会爆炸?直到达到那个记录才开始。

    与此同时,它正在将生成的结果流式传输回客户端。这有助于提高性能。

    你还期待什么?

        3
  •  1
  •   HLGEM    17 年前

        4
  •  0
  •   Andomar    17 年前

    你完美地描述了正在发生的事情,所以我不明白你想知道什么你还不知道?

    修复查询非常简单,只需让Sql Server将参数强制转换为varchar,而不是将varchar列强制转换为bigint:

    SELECT * FROM Phones WHERE Mobile = cast(@biMobile_1 as varchar(50))
    
        5
  •  0
  •   Rich.Carpenter    17 年前

    将 返回以查看您提交的查询是否对每一行有效,然后返回并再次执行查询以返回结果。

    查询执行和返回数据开始,直到找到一个证明无效的比较。

        6
  •  0
  •   John Sansom    17 年前

    作用 移动列(在本例中是从nvarchar到bigint的隐式转换)。当然,函数应用于 基础以及您所看到的行为,在cast函数失败之前,select语句不会爆炸。

    通过将正在搜索的单个数值变量(@biMobile_2)转换为nvarchar,而不是隐式地为所有行转换Mobile列,可以避免此问题。

    SELECT * FROM Phones WHERE Mobile = convert(nvarchar,@biMobile_2)