2019 年 7 月更新 - 根据 here,STRING_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/