繁体   English   中英

单 CTE 多 SELECT,CTE 在 SQL Server 中创建两次

[英]Single CTE with multiple SELECTs, CTE created twice in SQL Server

要求是在某些字段上连接(配对)两个表,然后按顺序显示配对结果。 配对正在 CTE 中完成。 一对被分配了一个 GUID,然后用于按顺序显示每对。 对于 Oracle (11.2),它按预期工作,但对于 SQL 服务器 (2017),似乎 CTE 创建了两次,因为每一行都有自己的 GUID。

我如何使用 CTE 解决它? 为什么 SQL 服务器的行为是这样的?

SQL 服务器http://sqlfiddle.com/#!18/21c167/1

Oracle http://sqlfiddle.com/#!4/c49dbb/2 (使用了 DBMS_RANDOM.RANDOM 而不是 SYS_GUID(),因为 SYS_GUID() 为 sqlfiddle.com 上的所有行返回了相同的值,同时在我的开发机器上运行良好)

CREATE TABLE VEHICLE (CODE NUMERIC(3,0), [TYPE] VARCHAR(50), DRIVER_CATEGORY VARCHAR(2));
CREATE TABLE DRIVER (CODE NUMERIC(9,0), NAME VARCHAR(200), CATEGORY VARCHAR(2));
 
INSERT INTO VEHICLE VALUES (1, 'LIMOUSINE', 'A');
INSERT INTO VEHICLE VALUES (2, 'TRUCK', 'B');
INSERT INTO VEHICLE VALUES (3, 'SUV', 'C');
INSERT INTO VEHICLE VALUES (4, 'SEDAN', 'C');
INSERT INTO VEHICLE VALUES (5, 'SPORTS CAR', 'D');
  
INSERT INTO DRIVER VALUES (1, 'James', 'A');
INSERT INTO DRIVER VALUES (2, 'Robert', 'B');
INSERT INTO DRIVER VALUES (3, 'John', 'C');
INSERT INTO DRIVER VALUES (4, 'Jennifer', 'C');
INSERT INTO DRIVER VALUES (5, 'Patricia', 'D');
INSERT INTO DRIVER VALUES (6, 'Susan', 'A');     
INSERT INTO DRIVER VALUES (7, 'Lisa', 'B');     

  ;WITH MATCHPAIRS AS 
  ( 
      SELECT V.CODE VEHICLE_CODE, D.CODE DRIVER_CODE, V.TYPE TYPE, V.DRIVER_CATEGORY, D.NAME, D.CATEGORY, NewID() PAIRID
      FROM VEHICLE V INNER JOIN DRIVER D ON V.DRIVER_CATEGORY = D.CATEGORY
  )
    SELECT p.VEHICLE_CODE, p.DRIVER_CODE, P.PAIRID, V.TYPE TYPE_NAME, 0 RECORD_ORDER  
    FROM VEHICLE V INNER JOIN MATCHPAIRS P ON V.CODE = P.VEHICLE_CODE 
    UNION ALL  
    SELECT p.VEHICLE_CODE, p.DRIVER_CODE, P.PAIRID, D.NAME TYPE_NAME, 1 RECORD_ORDER  
    FROM DRIVER D INNER JOIN MATCHPAIRS P ON D.CODE = P.DRIVER_CODE
    ORDER BY PAIRID, RECORD_ORDER

CTE 不会被缓存,它们会针对每个引用重新计算。

无论如何, NEWID有时会做一些奇怪的事情,当不在最外面的SELECT上使用时,所以最好使用临时表。 CROSS APPLYCROSS JOIN有时也不适用于NEWID

或者,改用不同的唯一化符。 例如

;WITH MATCHPAIRS AS (
    SELECT
      V.CODE VEHICLE_CODE,
      D.CODE DRIVER_CODE,
      V.TYPE TYPE,
      V.DRIVER_CATEGORY,
      D.NAME,
      D.CATEGORY,
      ROW_NUMBER() OVER (ORDER BY v.CODE, D.CODE) PAIRID
    FROM VEHICLE V
    INNER JOIN DRIVER D ON V.DRIVER_CATEGORY = D.CATEGORY
)
SELECT
  p.VEHICLE_CODE,
  p.DRIVER_CODE,
  P.PAIRID,
  V1.TYPE_NAME,
  v1.RECORD_ORDER  
FROM MATCHPAIRS P
CROSS APPLY (VALUES
  (p.TYPE, 0),
  (p.NAME, 1)
) V1(TYPE_NAME, RECORD_ORDER)
ORDER BY
  PAIRID,
  RECORD_ORDER;

请注意使用CROSS APPLY (VALUES to unpivot,这样可以节省两次读取 CTE。

SQL 小提琴

最简单的解决方案是使用临时表而不是 CTE。 如果需要,您可以将代码包装在存储过程中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM