【问题标题】:Query with t(n) and multiple cross joins使用 t(n) 和多个交叉连接进行查询
【发布时间】:2015-02-06 05:22:48
【问题描述】:

我试图从 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. 为什么我们在“--load 10000rows...”下面有一个“With”;那个“with”是做什么的?它是“创建”语句的一部分吗?

  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 中吗?我们为什么要这样做“选择...”?这意味着什么? 我知道REPLICATE(N'X', 4000) 会将X 4000 次放入Col1REPLICATE(CAST('X' AS nvarchar(MAX)), 10000)X 放入Col2 10000 次。如果我们这样做,那么为什么要从 t16M 中选择呢?或者如果我们从 t16M 中选择,那为什么要复制呢?

  4. 做什么 @RowCount = 计数(*) 做?将表中的行数分配给名为“RowCount”的变量?它与“@@RowCount”相同吗?我认为不可能。

【问题讨论】:

    标签: sql-server tsql


    【解决方案1】:

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

    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 JOINed 到自己 4 次,从而生成 4 * 4 * 4 * 44^4256 行,因此别名为 t256。同样t256CROSS 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 CREATE 语句下方的 WITH 关键字标记了 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 一样的结果。

    【讨论】:

    • 我仍然坚持一些事情(用我的问题(2)和(3)编辑了上面的原始帖子)。你能帮忙吗?
    猜你喜欢
    • 2021-03-31
    • 2019-10-01
    • 2012-12-31
    • 2013-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-02
    相关资源
    最近更新 更多