2

我有一张表要更新。要更新的值是数据表上 PK ID 的 FK。

数据表具有日期范围,并且正在更新的表具有出生日期字段(月、日、年)。我的 Update 语句可以遍历所有记录 RBAR(逐行排列)但我希望使用更多基于集合的解决方案。我已经尝试在 update 语句的 from 子句中使用 case 语句和表连接,但是关于这个问题的一些事情让我不知道如何处理它。这是表模式和我对更新语句的尝试

Table 1 Person:
TABLE [dbo].[TFI_PERSON](
    [PERSON_ID] [int] IDENTITY(3500,1) NOT NULL,
    [HOROSCOPE_SIGN_ID] [int] NULL,
    [DOB_DAY] [int] NOT NULL,
    [DOB_MONTH] [int] NOT NULL,
    [DOB_YEAR] [int] NOT NULL,

表 2 星座

TABLE [dbo].[TFI_HOROSCOPE_SIGN](
    [HOROSCOPE_SIGN_ID] [int] IDENTITY(1,1) NOT NULL,
    [HOROSCOPE_SIGN] [nvarchar](100) NOT NULL,
    [HOROSCOPE_BEGIN_DATE] [datetime] NOT NULL,
    [HOROSCOPE_END_DATE] [datetime] NOT NULL,

尝试 1 和 2

UPDATE P
SET P.HOROSCOPE_SIGN_ID = HS.[HOROSCOPE_SIGN_ID] 
                FROM dbo.TFI_PERSON AS P JOIN [dbo].[TFI_HOROSCOPE_SIGN] AS HS
                    ON P.[HOROSCOPE_SIGN_ID] = HS.[HOROSCOPE_SIGN_ID] 
WHERE 
 CAST(DOB_YEAR AS NVARCHAR)+ '-' + CAST(DOB_MONTH AS NVARCHAR) + '-' + CAST(DOB_DAY AS NVARCHAR) BETWEEN HS.[HOROSCOPE_BEGIN_DATE] AND HS.[HOROSCOPE_END_DATE] 

UPDATE dbo.TFI_PERSON
SET HOROSCOPE_SIGN_ID = (SELECT HOROSCOPE_SIGN_ID 
    FROM dbo.TFI_HOROSCOPE_SIGN 
    WHERE CAST(CAST(DOB_YEAR AS NVARCHAR)+ '/' + CAST(DOB_MONTH AS NVARCHAR) + '/' + CAST(DOB_DAY AS NVARCHAR) AS DATETIME) BETWEEN [HOROSCOPE_BEGIN_DATE] AND [HOROSCOPE_END_DATE] )

感谢您的协助。

4

2 回答 2

2

我的想法是这样的:

UPDATE P
SET HOROSCOPE_SIGN_ID = HS.[HOROSCOPE_SIGN_ID] 
FROM dbo.TFI_PERSON AS P 
JOIN [dbo].[TFI_HOROSCOPE_SIGN] AS HS
ON CAST(CAST((P.DOB_DAY + P.DOB_MONTH * 100 + P.DOB_YEAR * 10000) AS char(8)) AS datetime) 
   BETWEEN HS.[HOROSCOPE_BEGIN_DATE] AND HS.[HOROSCOPE_END_DATE]

当然,这假设 TFI_HOROSCOPE_SIGN 表的日期范围涵盖了 TFI_Person 中所有可能的人的出生日期。

于 2013-06-22T23:36:31.573 回答
0

TL;DR:您的更新不是 RBAR,因为基于集合的方法将生成相同的查询计划,导致 SQL Server 将以与基于集合的方法完全相同的方式执行它们。

我不同意您的更新语句正在更新 RBAR。RBAR 的想法是在你的语句中做一些循环构造,比如游标或临时表(https://www.simple-talk.com/sql/t-sql-programming/rbar--row-by-痛苦的行/)。集合操作的想法是让 SQL Server 循环遍历行。在这些简单的子查询中,优化器非常聪明地判断出适当的执行是连接,无论您是否声明它。

SQL Server 不存在将所有行神奇地更新为“集合”的方法。所有基于集合的解决方案都将生成一个查询计划,SQL Server 最终将需要逐行执行操作。由于各个行存储在不同的位置,因此需要有人遍历它们。在 RBAR 方法中,客户端显式地遍历它们。在基于集合的方法中,SQL Server 隐式地遍历它们。只要你没有 SQL 中的 WHILE 或 C# 中的 foreach 之类的循环,那么你就没有真正在做 RBAR。由于两个更新语句都没有显式循环,因此您没有执行更新 RBAR。当您并排查看更新的查询计划时,这一点很清楚。

我用 OP 的两个查询、一个显式连接查询和@GliM创建了一个小提琴 ( http://sqlfiddle.com/#!3/6c6a6/6 ),以显示所有四个查询都作为连接执行。这些都是循环连接,但如果有正确的索引和统计信息,优化器会将它们转换为适当的连接。我认为它们之间的性能不会有显着差异,特别是因为其中 3/4 具有完全相同的查询计划。

这里有一个有趣的区别。第二个查询是左外连接,其他查询是内连接。在第二个查询中,所有行都将被更新。对于没有匹配星座的行,id 将被更新为 NULL。在其他查询中,没有匹配星座的行将保留它们之前的任何 id。您也可以将具有显式联接的查询更改为左联接。

编辑:

我之前不是很明确。我添加了很多说明以表明 OP 的查询不是 RBAR,因为所有单语句查询最终都将使用类似的执行计划执行。

于 2013-06-22T22:17:31.730 回答