简体   繁体   English

SQL Server:在uniqueidentifier上加入

[英]SQL Server : join on uniqueidentifier

I have two tables Backup and Requests . 我有两个表BackupRequests

Below is the script for both the tables 以下是两个表的脚本

Backup 后备

CREATE TABLE UserBackup(
           FileName varchar(70) NOT NULL,
        )

File name is represented by a guid. 文件名由GUID表示。 Sometimes there is some additional information related to the file. 有时还有一些与文件有关的其他信息。 Hence we have entries like guid_ADD entried in table. 因此,我们在表中输入了类似guid_ADD的条目。

Requests 要求

CREATE TABLE Requests(
           RequestId UNIQUEIDENTIFIER NOT NULL,
           Status int Not null
        )

Here are some sample rows : 以下是一些示例行:

UserBackup table: UserBackup表:

FileName
15b993cc-e8be-405d-bb9f-0c58b66dcdfe 
4cffe724-3f68-4710-b785-30afde5d52f8 
4cffe724-3f68-4710-b785-30afde5d52f8_Add
7ad22838-ddee-4043-8d1f-6656d2953545

Requests table: 请求表:

RequestId                              Status
15b993cc-e8be-405d-bb9f-0c58b66dcdfe    1
4cffe724-3f68-4710-b785-30afde5d52f8    1
7ad22838-ddee-4043-8d1f-6656d2953545    2

What I need is to return all the rows from userbackup table whose name (the guid) is matches RequestId in the Requests table and the status is 1. So here is the query I wrote 我需要从userbackup表中返回所有行,该表的名称(GUID)与Requests表中的RequestId匹配,并且状态为1。所以这是我编写的查询

Select * 
from UserBackup
inner join Requests on UserBackup.FileName = Requests.RequestId
where Requests.Status = 1

And this works fine. 这很好。 It returns me the following result 它给我以下结果

FileName                                      RequestId                              Status
15b993cc-e8be-405d-bb9f-0c58b66dcdfe          15b993cc-e8be-405d-bb9f-0c58b66dcdfe     1
4cffe724-3f68-4710-b785-30afde5d52f8          4cffe724-3f68-4710-b785-30afde5d52f8     1
4cffe724-3f68-4710-b785-30afde5d52f8_Add      4cffe724-3f68-4710-b785-30afde5d52f8     1

This is exactly what I want. 这正是我想要的。 But what I don't understand is how it is working. 但是我不明白它是如何工作的。 If you notice the result is returning 4cffe724-3f68-4710-b785-30afde5d52f8_Add row as well. 如果您注意到结果也返回4cffe724-3f68-4710-b785-30afde5d52f8_Add行。 The inner join is on varchar and uniqueidentifier , and this join instead of working like "Equals to" comparison works like "contains" comparison. 内部uniqueidentifier位于varcharuniqueidentifier ,该uniqueidentifier而不是像“等于”比较那样工作,而像“包含”比较那样工作。 I want to know how this works so that I can be sure to use this code without any unexpected scenarios. 我想知道它是如何工作的,以便可以确保在没有任何意外情况的情况下使用此代码。

The values on both sides of a comparison have to be of the same data type. 比较两侧的值必须是相同的数据类型。 There's no such thing as, say, comparing a uniqueidentifier and a varchar . 例如,没有比对uniqueidentifiervarchar比较。

uniqueidentifier has a higher precedence than varchar so the varchar s will be converted to uniqueidentifier s before the comparison occurs. uniqueidentifier优先级高于varchar因此在进行比较之前, varchar将被转换为uniqueidentifier

Unfortunately, you get no error or warning if the string contains more characters than are needed: 不幸的是,如果该字符串包含的字符数超过所需数量,则不会出现任何错误或警告:

select CONVERT(uniqueidentifier,'4cffe724-3f68-4710-b785-30afde5d52f8_Add')

Result: 结果:

4CFFE724-3F68-4710-B785-30AFDE5D52F8

If you want to force the comparison to occur between strings, you'll have to perform an explicit conversion: 如果要强制在字符串之间进行比较,则必须执行显式转换:

Select * 
from UserBackup
inner join Requests
on UserBackup.FileName = CONVERT(varchar(70),Requests.RequestId)
where Requests.Status = 1

When you compare two columns of different data types SQL Server will attempt to do implicit conversion on lower precedence. 当您比较不同数据类型的两列时,SQL Server将尝试以较低的优先级进行隐式转换。

The following comes from MSDN docs on uniqueidentifier 以下是来自uniqueidentifier MSDN文档

The following example demonstrates the truncation of data when the value is too long for the data type being converted to. 下面的示例演示了当值对于要转换为的数据类型而言太长时,数据将被截断。 Because the uniqueidentifier type is limited to 36 characters, the characters that exceed that length are truncated. 因为uniqueidentifier类型限制为36个字符,所以超过该长度的字符将被截断。

DECLARE @ID nvarchar(max) = N'0E984725-C51C-4BF4-9960-E1C80E27ABA0wrong'; 
SELECT @ID, CONVERT(uniqueidentifier, @ID) AS TruncatedValue;

http://msdn.microsoft.com/en-us/library/ms187942.aspx http://msdn.microsoft.com/en-us/library/ms187942.aspx

Documentation is clear that data is truncated 文档清楚数据被截断

When ever you are unsure about your join operation you can verify Actual Execution Plan . 每当您不确定联接操作时,都可以验证“ Actual Execution Plan

Here is test sample that you can run inside SSMS or SQL Sentry Plan Explorer 这是可以在SSMS或SQL Sentry Plan Explorer中运行的测试示例

DECLARE @userbackup TABLE ( _FILENAME VARCHAR(70) )

INSERT INTO @userbackup
    VALUES  ( '15b993cc-e8be-405d-bb9f-0c58b66dcdfe' ),
            ( '4cffe724-3f68-4710-b785-30afde5d52f8' ),
            ( '4cffe724-3f68-4710-b785-30afde5d52f8_Add' )
,           ( '7ad22838-ddee-4043-8d1f-6656d2953545' )


DECLARE @Requests TABLE
    (
     requestID UNIQUEIDENTIFIER
    ,_Status INT
    )
INSERT INTO @Requests
    VALUES  ( '15b993cc-e8be-405d-bb9f-0c58b66dcdfe', 1 )
,           ( '4cffe724-3f68-4710-b785-30afde5d52f8', 1 )
,           ( '7ad22838-ddee-4043-8d1f-6656d2953545', 2 )

SELECT *
    FROM @userbackup u
    JOIN @Requests r
        ON u.[_FILENAME] = r.requestID
    WHERE r.[_Status] = 1

Instead of regular join operation SQL Server is doing HASH MATCH with EXPR 1006 in SSMS it is hard to see what is doing but if you open XML file you will find this 而不是常规join运行SQL Server正在做HASH MATCHEXPR 1006 SSMS中是很难看到什么做什么,但如果你打开XML文件,你会发现这个

<ColumnReference Column="Expr1006" />
<ScalarOperator ScalarString="CONVERT_IMPLICIT(uniqueidentifier,@userbackup.[_FILENAME] as [u].[_FILENAME],0)">

When ever in doubt check execution plan and always make sure to match data types when comparing. 如有疑问,请检查执行计划,并在比较时始终确保匹配数据类型。

This is great blog Data Mismatch on WHERE Clause might Cause Serious Performance Problems from Microsoft engineer on exact problem. 这是很棒的博客,WHERE子句上的数据不匹配可能会导致 Microsoft工程师针对确切问题产生严重的性能问题。

What is happening here is the FileName is being converted from varchar to a UniqueIdentifier, and during that process it ignores anything after the first 36 characters. 这里发生的是FileName正在从varchar转换为UniqueIdentifier,并且在此过程中,它将忽略前36个字符之后的任何内容。

You can see it in action here 您可以在这里看到它的运行情况

Select convert(uniqueidentifier, UserBackup.FileName), FileName
  from UserBackup

It works, but to reduce confusion for the next person to come along, you might want to store the RequestId associated with the UserBackup as a GUID in the UserBackup table and join on that. 它可以工作,但是为了减少下一个人的困惑,您可能希望将与UserBackup关联的RequestId作为GUID存储在UserBackup表中并加入。

At the very least put a comment in ;) 至少在;)中发表评论;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM