使用t(n)和多个交叉连接进行查询

时间:2015-02-06 05:22:48

标签: sql-server tsql

我试图从Stackoverflow线程中理解这个查询:

--create test table
CREATE TABLE dbo.TestTable(
      Col1 nchar(4000) NOT NULL
    , Col2 nvarchar(MAX) NOT NULL
    );

--load 10000 rows (about 2.8GB)
WITH 
    t4 AS (SELECT n FROM (VALUES(0),(0),(0),(0)) t(n))
    ,t256 AS (SELECT 0 AS n FROM t4 AS a CROSS JOIN t4 AS b CROSS JOIN t4 AS c CROSS JOIN t4 AS d)
    ,t16M AS (SELECT ROW_NUMBER() OVER (ORDER BY (a.n)) AS num FROM t256 AS a CROSS JOIN t256 AS b CROSS JOIN t256 AS c)
INSERT INTO dbo.TestTable WITH(TABLOCKX) (Col1, Col2)
SELECT REPLICATE(N'X', 4000), REPLICATE(CAST('X' AS nvarchar(MAX)), 10000)
FROM t16M
WHERE num <= 100000;
GO

--run query in loop (expect parallel execution plan with many read-ahead and LOB page reads)
SET NOCOUNT ON;
DECLARE @RowCount int, @Iteration int = 1;
WHILE @Iteration <= 100
BEGIN
    CHECKPOINT;
    DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
    SELECT @RowCount = COUNT(*) FROM dbo.TestTable WHERE Col2 LIKE 'X%';
    RAISERROR('Iteration %d completed',0,1,@Iteration) WITH NOWAIT; --display progress message
    SET @Iteration += 1;
END;
GO
  1. 我特别无法理解t-sql查询的这一部分:

    --load 10000 rows (about 2.8GB)
    WITH 
        t4 AS (SELECT n FROM (VALUES(0),(0),(0),(0)) t(n))
        ,t256 AS (SELECT 0 AS n FROM t4 AS a CROSS JOIN t4 AS b CROSS JOIN t4 AS c CROSS JOIN t4 AS d)
        ,t16M AS (SELECT ROW_NUMBER() OVER (ORDER BY (a.n)) AS num FROM t256 AS a CROSS JOIN t256 AS b CROSS JOIN t256 AS c)
    INSERT INTO dbo.TestTable WITH(TABLOCKX) (Col1, Col2)
    SELECT REPLICATE(N'X', 4000), REPLICATE(CAST('X' AS nvarchar(MAX)), 10000)
    FROM t16M
    WHERE num <= 100000;
    GO
    
  2. 为什么我们有&#34; With&#34;在&#34; - 加载10000rows ...&#34; ;这是什么&#34;与&#34;做?它是&#39;创造&#39;的一部分?声明?

  3. 关于此插入声明:

    INSERT INTO dbo.TestTable WITH(TABLOCKX) (Col1, Col2)
    SELECT REPLICATE(N'X', 4000), REPLICATE(CAST('X' AS nvarchar(MAX)), 10000)
    FROM t16M
    WHERE num <= 100000;
    

    不会

    INSERT INTO dbo.TestTable WITH(TABLOCKX) (Col1, Col2)
    FROM t16M
    WHERE num <= 100000;
    

    做什么?以上不会从t16M获取前10000行并插入dbo.TestTable吗?为什么我们这样做&#34;选择...&#34;?这意味着什么? 我了解REPLICATE(N'X', 4000) XCol1 REPLICATE(CAST('X' AS nvarchar(MAX)), 10000) X Col2 {{1}} {{1}} {{1}} {{1}}。如果我们这样做,为什么选择t16M?或者如果我们从t16M中选择,那么为什么这会复制呢?

  4. 是什么 @RowCount = COUNT(*) 做?将表中的行数分配给名为&#39; RowCount&#39;的变量是否与&#39; @@ RowCount&#39;?相同?我不认为可以。

1 个答案:

答案 0 :(得分:3)

我会重新格式化代码以便更好地理解:

WITH t4 AS(
    SELECT n FROM (VALUES(0),(0),(0),(0)) t(n)
)
,t256 AS(
    SELECT 0 AS n 
    FROM t4 AS a 
    CROSS JOIN t4 AS b 
    CROSS JOIN t4 AS c 
    CROSS JOIN t4 AS d
)
,t16M AS(
    SELECT ROW_NUMBER() OVER (ORDER BY (a.n)) AS num 
    FROM t256 AS a 
    CROSS JOIN t256 AS b 
    CROSS JOIN t256 AS c
)
INSERT INTO dbo.TestTable WITH(TABLOCKX) (Col1, Col2)
SELECT REPLICATE(N'X', 4000), REPLICATE(CAST('X' AS nvarchar(MAX)), 10000)
FROM t16M
WHERE num <= 100000;

3个级联CTE组成了Tally Table或其他名为Numbers Table的人。它由从1到某个数字的序号组成。

这个生成4行,值为0:

WITH t4 AS(
    SELECT n FROM (VALUES(0),(0),(0),(0)) t(n)
)

然后它CROSS JOIN自身4次,从而生成4 * 4 * 4 * 44^4256行,因此生成别名t256。同样t256 CROSS JOIN为自己3次,产生16,777,216行,因此产生别名t16M。 如果您执行SELECT * FROM t16M,则可以验证它是否返回超过16M的行。

然后使用它将100000行插入TestTable,如where子句所示:

INSERT INTO dbo.TestTable WITH(TABLOCKX) (Col1, Col2)
SELECT REPLICATE(N'X', 4000), REPLICATE(CAST('X' AS nvarchar(MAX)), 10000)
FROM t16M
WHERE num <= 100000

有些人可能会使用WHILE循环来尝试执行此操作,即在表中插入100000行。 Tally Table是以基于集合的方式执行此操作的好方法。有关详细信息,请阅读:http://www.sqlservercentral.com/articles/T-SQL/62867/


问题#2 WITH语句下方的CREATE关键字标记Common Table Expression的声明。

问题#3 我相信下面的查询会产生语法错误。

INSERT INTO dbo.TestTable WITH(TABLOCKX) (Col1, Col2)
FROM t16M
WHERE num <= 100000;

另一方面,这不会。

INSERT INTO dbo.TestTable WITH(TABLOCKX) (Col1, Col2)
SELECT REPLICATE(N'X', 4000), REPLICATE(CAST('X' AS nvarchar(MAX)), 10000)
FROM t16M
WHERE num <= 100000;

此查询的作用是INSERT 100000行,由2列组成,其值为'X'的连续字符串。请记住,t16M是我们的Tally Table,其中包含从1到16M +的数字序列。我们不对插入使用Tally Table的值,我们只使用其行的存在来限制插入的数量。

问题#4 当你说@RowCount = COUNT(*)为变量分配行数时,你是对的。

SELECT @RowCount = COUNT(*) FROM dbo.TestTable WHERE Col2 LIKE 'X%';
SELECT @@ROWCOUNT

但是,上述说法并不相同。 @@ROWCOUNT返回受最后一个语句影响的行数。如果我们将其放在WHILE之后的SELECT @RowCount循环中,它将返回1,因为只有1行受到影响。但是,如果我们将其直接放在INSERT语句之后,它将返回与SELECT COUNT(*) FROM dbo.TestTable相同的内容。