【问题标题】:UPDATE FROM in Azure SQL DW?从 Azure SQL DW 更新?
【发布时间】:2018-09-05 23:56:15
【问题描述】:

我在尝试执行 UPDATE FROM 查询的 Azure SQL DW 中收到错误消息。错误是“UPDATE 和 DELETE 语句中的 FROM 子句不能包含子查询源或连接”

这只是特定于 SQL DW 吗?否则,我认为此查询没有任何问题。如果是 SQL DW 的限制,还有什么办法?

-- Permanent fact table with 5 billion rows
CREATE TABLE FactTable (Id1 INT, Id2 INT, EmailAddress NVARCHAR(100), Value1 INT)
WITH (DISTRIBUTION = HASH(EmailAddress));

-- Staging fact table with 10 million rows    
CREATE TABLE StageTable (Id1 INT, Id2 INT, EmailAddress NVARCHAR(100), Value1 INT)
WITH (DISTRIBUTION = HASH(EmailAddress), HEAP);

-- Add a secondary index that should help with joining to StageTable
CREATE NONCLUSTERED INDEX ix ON FactTable (Id1, Id2);

UPDATE fact
SET
   Value1 = CASE WHEN stage.Value1 > fact.Value1 THEN stage.Value1 ELSE fact.Value1 END
FROM FactTable AS fact
INNER JOIN StageTable AS stage ON fact.Id1 = stage.Id1 AND fact.Id2 = stage.Id2

【问题讨论】:

    标签: sql-update azure-sqldw


    【解决方案1】:

    根据the documentation,Azure SQL 数据仓库确实支持UPDATE,但不支持FROM 子句中的ANSI 联接。您可以使用 CTAS 来解决。简单的两表更新:

    UPDATE dbo.FactTable
    SET
       Value1 = CASE WHEN stage.Value1 > dbo.FactTable.Value1 THEN stage.Value1 ELSE dbo.FactTable.Value1 END
    FROM dbo.StageTable AS stage
    WHERE dbo.FactTable.Id1 = stage.Id1 
      AND dbo.FactTable.Id2 = stage.Id2;
    

    更复杂的 CTAS 示例,从main UPDATE documentation page 批发复制:

    -- Create an interim table
    CREATE TABLE CTAS_acs
    WITH (DISTRIBUTION = ROUND_ROBIN)
    AS
    SELECT  ISNULL(CAST([EnglishProductCategoryName] AS NVARCHAR(50)),0)    AS [EnglishProductCategoryName]
    ,       ISNULL(CAST([CalendarYear] AS SMALLINT),0)                      AS [CalendarYear]
    ,       ISNULL(CAST(SUM([SalesAmount]) AS MONEY),0)                     AS [TotalSalesAmount]
    FROM    [dbo].[FactInternetSales]       AS s
    JOIN    [dbo].[DimDate]                 AS d    ON s.[OrderDateKey]             = d.[DateKey]
    JOIN    [dbo].[DimProduct]              AS p    ON s.[ProductKey]               = p.[ProductKey]
    JOIN    [dbo].[DimProductSubCategory]   AS u    ON p.[ProductSubcategoryKey]    = u.[ProductSubcategoryKey]
    JOIN    [dbo].[DimProductCategory]      AS c    ON u.[ProductCategoryKey]       = c.[ProductCategoryKey]
    WHERE   [CalendarYear] = 2004
    GROUP BY
            [EnglishProductCategoryName]
    ,       [CalendarYear]
    ;
    
    -- Use an implicit join to perform the update
    UPDATE  AnnualCategorySales
    SET     AnnualCategorySales.TotalSalesAmount = CTAS_ACS.TotalSalesAmount
    FROM    CTAS_acs
    WHERE   CTAS_acs.[EnglishProductCategoryName] = AnnualCategorySales.[EnglishProductCategoryName]
    AND     CTAS_acs.[CalendarYear]               = AnnualCategorySales.[CalendarYear]
    ;
    
    --Drop the interim table
    DROP TABLE CTAS_acs
    ;
    

    【讨论】:

    • 谢谢。这可行,但比我想要的要慢。我想知道我是否可以让系统使用另一个索引或最小化分布之间的数据移动。
    • 你能发布一些更真实的 DDL,例如分布、数据量和样本数据吗?现在,通过最新版本的 SQL Server Management Studio (SSMS) 可以更轻松地对 Azure SQL 数据仓库进行计划分析 - 请参阅 here
    • 加上常见的 Azure DW 统计数据,你在运行什么 DWU 等等。请查看这篇优秀的文章:docs.microsoft.com/en-us/azure/sql-data-warehouse/…
    • 我在示例中添加了更多细节,包括我认为有助于排除比较的二级索引。 SSMS 表示计划忽略它。
    【解决方案2】:

    我发现使用 ASDW(和 APS/PDW)是一种很好的做法,可以避免像瘟疫一样的批量更新。

    这是一个纯粹的 CTAS 替代方案,在您更新大量行的情况下会更快。

    它假设 id1 是一个相对较好的分布键,并且暂存行的数量少于事实行,使得复制可行。这种策略应该消除节点之间的数据移动。

    如果您有一个非常大的临时表,则在每个表中创建一个 id1 和 id2 组合的代理列,然后按该列的哈希分布这两个表,将提供更好的性能。

    create  table FactTable (
            id1 int,
            id2 int,
            value1 int)
    with    (distribution = hash(id1));
    
    create  table StageTable (
            id1 int,
            id2 int,
            value1 int)
    with    (distribution = replicate);
    
    create  table UpdatedFact 
    with    (distribution = hash(id1)) 
    as
    select  f.id1,
            f.id2,
            case when s.id1 is not null and s.value1 > f.value1 
                then s.value1
                else f.value1
                end as value1
    from    FactTable f
            left outer join StageTable s
            on s.id1 = f.id1
            and s.id2 = f.id2
    
    truncate table FactTable;
    alter table UpdatedFact switch to FactTable;
    drop table UpdatedFact;
    

    【讨论】:

    • 罗恩的好例子
    • 我会尝试这种方法,但我怀疑 CTAS 是否会比简单地更新行更有效。我的事实表有大约 50 亿行,每次更新会增加大约 400 万行,更新大约 100 万行。
    • 请记住,CTAS 可以通过使用 FULL OUTER JOIN 在同一通道中创建新行,这将消除单独的 INSERT。当 LHS 为空时,使用完全外连接可以更新 RHS 中的值。 50 亿:500 万是一个有趣的比例,我认为你的怀疑是健康的 :)
    【解决方案3】:

    简化您的尝试会奏效。只需删除连接并从另一个表更新一个表。

    update FactTable
    set this = that
    from StageTable s where s.something = FactTable.something
    

    这是否是最好的方法取决于您的情况,但它会执行而不会引发错误。

    【讨论】:

      猜你喜欢
      • 2017-07-28
      • 2020-12-13
      • 2019-03-05
      • 1970-01-01
      • 2020-05-06
      • 2020-06-16
      • 1970-01-01
      • 2016-02-12
      • 2019-06-21
      相关资源
      最近更新 更多