【问题标题】:Does Azure SQL Data Warehouse have a way to split strings?Azure SQL 数据仓库是否可以拆分字符串?
【发布时间】:2017-08-11 12:36:41
【问题描述】:

做一些研究,我发现在 Azure SQL 数据仓库中没有很好的拆分字符串的选项。它没有新的 STRING_SPLIT() 函数或 OPENJSON() 函数。它也不允许用户定义函数中的 SELECT 语句尝试创建您自己的,就像社区制作的许多自定义拆分器函数一样。

因此,我想我会提出以下问题:SQL 数据仓库是否有拆分字符串的方法以及这里的最佳选择是什么?

用例

您在 SQL 表中有一个字段,其值为“My_Value_Is_Good”。目标是在 SELECT 语句中使用下划线分隔符将每个段拆分为单独的字段,或者最多写入新表。

我使用过的解决方案

对我来说,最主要的就是在数据进入数据仓库之前对其进行转换。我使用 Python 来解析数据。但是,更大的数据集确实会减慢这一速度,并且一旦进入系统,就会将其更多地隔离到特定的记录中。

【问题讨论】:

    标签: sql-server azure-sql-database data-warehouse


    【解决方案1】:

    2019 年 7 月更新 - 根据 hereSTRING_SPLIT 现在在 Azure SQL 数据仓库中可用。所以在我下面的例子中,代码会更像这样:

    DECLARE @delimiter CHAR(1) = '-';
    
    CREATE TABLE dbo.guids_split
    WITH
    (
        DISTRIBUTION = HASH(xguid),
        HEAP
    )
    AS
    SELECT *
    FROM dbo.guids g
        CROSS APPLY STRING_SPLIT ( xguid, @delimiter );
    

    与普通 SQL Server 或 Azure SQL 数据库相比,Azure SQL 数据仓库的 T-SQL 表面积减少了。它没有任何花哨的技巧,例如STRING_SPLIT、表值函数、CLR、XML;甚至游标也是不允许的。事实上,对于有关该主题的一篇入门文章(SQL 2016 之前)“Split strings the right way - or the next best way”中的所有技术,除了数字表之外,您不能使用其中任何一种。

    因此,我们需要一些更程序化的东西,避免任何形式的循环。我已经使用上面的文章获得灵感,使用了测试数据脚本的改编版本和this approach

    -- Create one million guids
    IF OBJECT_ID('dbo.numbers') IS NOT NULL DROP TABLE dbo.numbers
    IF OBJECT_ID('dbo.guids_split') IS NOT NULL DROP TABLE dbo.guids_split
    IF OBJECT_ID('dbo.guids') IS NOT NULL DROP TABLE dbo.guids
    IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp
    GO
    
    
    CREATE TABLE dbo.Numbers (
        Number  INT NOT NULL
    )
    WITH
    (
        DISTRIBUTION = ROUND_ROBIN,     --!!TODO try distibuting?
        CLUSTERED INDEX ( Number )
    )
    GO
    
    
    DECLARE @UpperLimit INT = 1000000;
    
    ;WITH n AS
    (
        SELECT
            x = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
        FROM       sys.all_objects AS s1
        CROSS JOIN sys.all_objects AS s2
        CROSS JOIN sys.all_objects AS s3
    )
    SELECT x
    INTO #tmp
    FROM n
    WHERE x BETWEEN 1 AND @UpperLimit
    GO
    
    INSERT INTO dbo.Numbers ( Number )
    SELECT x
    FROM #tmp
    GO
    
    
    CREATE TABLE dbo.guids (
        rn  INT IDENTITY,
        xguid   CHAR(36) NOT NULL
    )
    WITH
    (
        DISTRIBUTION = HASH(xguid),
        CLUSTERED COLUMNSTORE INDEX
    )
    GO
    
    INSERT INTO dbo.guids ( xguid )
    SELECT NEWID() xguid
    FROM dbo.Numbers
    GO -- 10    -- scale up 10 to 100, 1,000 etc
    
    ALTER INDEX ALL ON dbo.guids REBUILD 
    GO
    
    
    -- Create the stats
    CREATE STATISTICS _st_numbers_number ON dbo.numbers (number);
    CREATE STATISTICS _st_guids_rn ON dbo.guids (rn);
    CREATE STATISTICS _st_guids_xguid ON dbo.guids (xguid);
    GO
    -- multi-col stat?
    :exit
    
    
    -- NB The length of the guid; so we don't have to use VARCHAR(MAX)
    DECLARE @delimiter VARCHAR(1) = '-';
    
    CREATE TABLE dbo.guids_split
    WITH
    (
        DISTRIBUTION = HASH(xguid),
        HEAP
    )
    AS
    SELECT
        s.rn,
        n.Number n,
        originalid AS xguid,
        LTRIM( RTRIM( SUBSTRING( s.xguid, n.Number + 1, CHARINDEX( @delimiter, s.xguid, n.Number + 1 ) - n.Number - 1 ) ) ) AS split_value
    FROM (
        SELECT
            rn,
            xguid AS originalid,
            CAST( CAST( @delimiter AS VARCHAR(38) ) + CAST( xguid AS VARCHAR(38) ) + CAST( @delimiter AS VARCHAR(38) ) AS VARCHAR(38) ) AS xguid
            FROM dbo.guids
            ) s
        CROSS JOIN dbo.Numbers n
    WHERE n.Number < LEN( s.xguid )
      AND SUBSTRING( s.xguid, n.Number, 1 ) = @delimiter;
    GO
    
    
    /*
    SELECT TOP 10 * FROM dbo.guids ORDER BY rn;
    
    SELECT *
    FROM dbo.guids_split
    WHERE rn In ( SELECT TOP 10 rn FROM dbo.guids ORDER BY rn )
    ORDER BY 1, 2;
    GO
    
    */
    

    该脚本现已在 ADW 上进行了测试,并令人满意地运行了超过 1 亿条记录。这仅在 DWU 400 上运行了不到 4 分钟(至少有一次我添加了统计数据并删除了 varchar(max) : )。然而,guids 是一个稍微人为的例子,因为数据的大小是统一的,并且总是只有 5 个部分可以拆分。

    从 Azure SQL 数据仓库中获得良好的性能实际上与通过良好的散列分布密钥最小化数据移动有关。因此,请发布一些真实的示例数据。

    另一种选择是 Azure 数据湖分析。 ADLA 支持“查询数据所在位置”的联合查询,因此您可以使用 U-SQL 查询原始表,使用本机 .net 方法将其拆分并输出 可以使用 Polybase 轻松导入。如果您需要有关此方法的更多帮助,请告诉我,我会举个例子。

    SQLCat 团队已经发表了这篇关于 SQL 数据仓库的反模式的文章,这种类型的字符串处理可能被认为是一个例子。请阅读这篇文章:

    https://blogs.msdn.microsoft.com/sqlcat/2017/09/05/azure-sql-data-warehouse-workload-patterns-and-anti-patterns/

    【讨论】:

    • 哇哦,正要赏金,结果看到有人回答!不幸的是,我确实有更大的数据(数十亿),但这并不意味着我没有更小的用例。让我测试一下,看看效果如何。出于清洁和合规目的,我完全可以扩展到 2000+。
    • 抱歉,没有使用 GUID 运行其他示例就发表了评论。我更喜欢那个例子。另一个在单个列中复制字符串,然后在所有行中复制。这很可怕。但是,GUID 示例在没有复制的情况下使用类似方法看起来有点不同?两者都返回单个字段拆分,但使用 GUID 示例,您可以将原始字符串与要更新的表匹配并使用顺序来表示位置(即:ROW_ID PARTITION BY STRING)。这可能会奏效。让我在其他一些数据上进行测试,看看效果如何。
    • REPLICATE 仅用于生成测试数据。
    • 你能展示一些真实的测试数据吗?从 Azure SQL 数据仓库中获得良好的性能实际上与通过良好的散列分布密钥最小化数据移动有关。
    • 是的,我今天要测试一下。我想我对你为什么在字段中复制它而不是像你对 GUID 示例所做的那样只是跨行复制它感到困惑。我将对十亿行进行测试,看看效果如何。
    猜你喜欢
    • 2020-05-28
    • 2018-04-30
    • 2021-01-09
    • 1970-01-01
    • 2016-06-30
    • 1970-01-01
    • 2017-03-07
    • 2017-08-14
    • 2016-10-26
    相关资源
    最近更新 更多