SQL 服务器 CTE 循环;一起插入所有记录
SQL Server CTE loop; insert all record together
我有这种情况:
drop table #t1;
drop table #t2
select *
into #t1
from
(select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va4'c1,'vb4'c2,'vc4'c3) t
select *
into #t2
from #t1
where 0 = 1
;with tmp1 as
(
select
t1.*,
ROW_NUMBER() over (partition by t1.c1 order by (select null)) r
from
#t1 t1
left join
#t2 t2 on t1.c1 = t2.c1
where
t2.c1 is null
), tmp2 as
(
select
0 n,*
from
tmp1
union all
select
n+1 n, t1.c1, t1.c2, t1.c3, t1.r
from
tmp2 t1
join
tmp1 t2 on t1.c1 = t2.c1
and t2.r = t1.r + 1
where
n < 10
)
--insert #t2
select c1, c2, c3 --,r
from tmp2
当我运行这个时,它选择一切都很好(103条记录)。
问题是当我将此代码插入#t2(13 条记录!!!)
我认为 SQL 运行s 一步一步地在 运行ning 期间插入记录并且比我在 tmp1 中的条件结束...
如何解决?
我的目标是检查数据是否存在,然后循环并插入结果...但是 SQL 在第一个循环后停止...
您在 MS SQL 服务器的 CTE 实现中遇到了一个问题。并非所有后端都以这种方式处理。您必须先 select 进入临时游标,然后从中插入。即:
SELECT *
INTO #t1
FROM(
SELECT 'va1' c1, 'vb1' c2, 'vc1' c3
UNION ALL
SELECT 'va2' c1, 'vb2' c2, 'vc2' c3
UNION ALL
SELECT 'va3' c1, 'vb3' c2, 'vc3' c3
UNION ALL
SELECT 'va1' c1, 'vb1' c2, 'vc1' c3
UNION ALL
SELECT 'va2' c1, 'vb2' c2, 'vc2' c3
UNION ALL
SELECT 'va3' c1, 'vb3' c2, 'vc3' c3
UNION ALL
SELECT 'va1' c1, 'vb1' c2, 'vc1' c3
UNION ALL
SELECT 'va2' c1, 'vb2' c2, 'vc2' c3
UNION ALL
SELECT 'va3' c1, 'vb3' c2, 'vc3' c3
UNION ALL
SELECT 'va1' c1, 'vb1' c2, 'vc1' c3
UNION ALL
SELECT 'va2' c1, 'vb2' c2, 'vc2' c3
UNION ALL
SELECT 'va3' c1, 'vb3' c2, 'vc3' c3
UNION ALL
SELECT 'va4' c1, 'vb4' c2, 'vc4' c3
)t;
SELECT * INTO #t2 FROM #t1 WHERE 0=1;
DECLARE @tmp TABLE(c1 VARCHAR(10), c2 VARCHAR(10), c3 VARCHAR(10));
WITH
tmp1 AS (
SELECT t1.*, ROW_NUMBER() OVER (PARTITION BY t1.c1 ORDER BY(SELECT NULL)) r
FROM #t1 t1
LEFT JOIN #t2 t2 ON t1.c1=t2.c1
WHERE t2.c1 IS NULL
),
tmp2 AS (
SELECT 0 n, * FROM tmp1
UNION ALL
SELECT n+1 n, t1.c1, t1.c2, t1.c3, t1.r
FROM tmp2 t1
JOIN tmp1 t2 ON t1.c1=t2.c1
AND t2.r=t1.r+1
WHERE n<10
)
INSERT @tmp(c1, c2, c3)
SELECT c1, c2, c3 --,r
FROM tmp2;
INSERT #t2 SELECT * FROM @tmp;
SELECT * FROM #t2;
DROP TABLE #t1;
DROP TABLE #t2;
您可以使用 MERGE
:
select * into #t1
from(
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va4'c1,'vb4'c2,'vc4'c3
)t;
select * into #t2 from #t1 where 0=1;
;with tmp1 as(
select t1.*, ROW_NUMBER()over(partition by t1.c1 order by(select null))r
from #t1 t1
left join #t2 t2
on t1.c1=t2.c1
where t2.c1 is null
),tmp2 as (
select 0 n,*
from tmp1
union all
select n+1 n,t1.c1,t1.c2,t1.c3,t1.r
from tmp2 t1
join tmp1 t2
on t1.c1=t2.c1
and t2.r=t1.r+1
where n<10
)
MERGE #t2
USING tmp2
ON #t2.c1 = tmp2.c1
WHEN NOT MATCHED THEN
INSERT VALUES (tmp2.c1, tmp2.c2, tmp2.c3);
SELECT @@ROWCOUNT;
-- 103
编辑:
感谢 Bartosz Ratajczyk 检查此案例:
原来是和lazy/eager table/index假脱机有关。至少还有两种方法可以强制SQL服务器生成不同的执行计划:
a) 通过使用 TOP (100) PERCENT
DECLARE @n INT = 100;
;with tmp1 as (
select t1.*,
ROW_NUMBER()over(partition by t1.c1 order by(select null))r
from #t1 t1
left join #t2 t2
on t1.c1=t2.c1
where t2.c1 is null
),tmp2 as
(
select 0 n,*
from tmp1
union all
select n+1 n,t1.c1,t1.c2,t1.c3,t1.r
from tmp2 t1
join tmp1 t2
on t1.c1=t2.c1
and t2.r=t1.r+1
where n<10
)
insert #t2
select TOP (@n) PERCENT c1, c2, c3 --,r
from tmp2
SELECT @@ROWCOUNT;
b) 通过使用 ORDER BY .. OFFSET 0 ROWS
:
;with tmp1 as(
select t1.*,
ROW_NUMBER()over(partition by t1.c1 order by(select null))r
from #t1 t1
left join #t2 t2
on t1.c1=t2.c1
where t2.c1 is null
),tmp2 as
(
select 0 n,*
from tmp1
union all
select n+1 n,t1.c1,t1.c2,t1.c3,t1.r
from tmp2 t1
join tmp1 t2
on t1.c1=t2.c1
and t2.r=t1.r+1
where n<10
)
insert #t2
select c1, c2, c3 --,r
from tmp2
ORDER BY 1 OFFSET 0 ROWS;
SELECT @@ROWCOUNT;
附录:How does the recursive CTE work? by Bartosz Ratajczyk
我有这种情况:
drop table #t1;
drop table #t2
select *
into #t1
from
(select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va4'c1,'vb4'c2,'vc4'c3) t
select *
into #t2
from #t1
where 0 = 1
;with tmp1 as
(
select
t1.*,
ROW_NUMBER() over (partition by t1.c1 order by (select null)) r
from
#t1 t1
left join
#t2 t2 on t1.c1 = t2.c1
where
t2.c1 is null
), tmp2 as
(
select
0 n,*
from
tmp1
union all
select
n+1 n, t1.c1, t1.c2, t1.c3, t1.r
from
tmp2 t1
join
tmp1 t2 on t1.c1 = t2.c1
and t2.r = t1.r + 1
where
n < 10
)
--insert #t2
select c1, c2, c3 --,r
from tmp2
当我运行这个时,它选择一切都很好(103条记录)。
问题是当我将此代码插入#t2(13 条记录!!!)
我认为 SQL 运行s 一步一步地在 运行ning 期间插入记录并且比我在 tmp1 中的条件结束...
如何解决?
我的目标是检查数据是否存在,然后循环并插入结果...但是 SQL 在第一个循环后停止...
您在 MS SQL 服务器的 CTE 实现中遇到了一个问题。并非所有后端都以这种方式处理。您必须先 select 进入临时游标,然后从中插入。即:
SELECT *
INTO #t1
FROM(
SELECT 'va1' c1, 'vb1' c2, 'vc1' c3
UNION ALL
SELECT 'va2' c1, 'vb2' c2, 'vc2' c3
UNION ALL
SELECT 'va3' c1, 'vb3' c2, 'vc3' c3
UNION ALL
SELECT 'va1' c1, 'vb1' c2, 'vc1' c3
UNION ALL
SELECT 'va2' c1, 'vb2' c2, 'vc2' c3
UNION ALL
SELECT 'va3' c1, 'vb3' c2, 'vc3' c3
UNION ALL
SELECT 'va1' c1, 'vb1' c2, 'vc1' c3
UNION ALL
SELECT 'va2' c1, 'vb2' c2, 'vc2' c3
UNION ALL
SELECT 'va3' c1, 'vb3' c2, 'vc3' c3
UNION ALL
SELECT 'va1' c1, 'vb1' c2, 'vc1' c3
UNION ALL
SELECT 'va2' c1, 'vb2' c2, 'vc2' c3
UNION ALL
SELECT 'va3' c1, 'vb3' c2, 'vc3' c3
UNION ALL
SELECT 'va4' c1, 'vb4' c2, 'vc4' c3
)t;
SELECT * INTO #t2 FROM #t1 WHERE 0=1;
DECLARE @tmp TABLE(c1 VARCHAR(10), c2 VARCHAR(10), c3 VARCHAR(10));
WITH
tmp1 AS (
SELECT t1.*, ROW_NUMBER() OVER (PARTITION BY t1.c1 ORDER BY(SELECT NULL)) r
FROM #t1 t1
LEFT JOIN #t2 t2 ON t1.c1=t2.c1
WHERE t2.c1 IS NULL
),
tmp2 AS (
SELECT 0 n, * FROM tmp1
UNION ALL
SELECT n+1 n, t1.c1, t1.c2, t1.c3, t1.r
FROM tmp2 t1
JOIN tmp1 t2 ON t1.c1=t2.c1
AND t2.r=t1.r+1
WHERE n<10
)
INSERT @tmp(c1, c2, c3)
SELECT c1, c2, c3 --,r
FROM tmp2;
INSERT #t2 SELECT * FROM @tmp;
SELECT * FROM #t2;
DROP TABLE #t1;
DROP TABLE #t2;
您可以使用 MERGE
:
select * into #t1
from(
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va1'c1,'vb1'c2,'vc1'c3 union all
select 'va2'c1,'vb2'c2,'vc2'c3 union all
select 'va3'c1,'vb3'c2,'vc3'c3 union all
select 'va4'c1,'vb4'c2,'vc4'c3
)t;
select * into #t2 from #t1 where 0=1;
;with tmp1 as(
select t1.*, ROW_NUMBER()over(partition by t1.c1 order by(select null))r
from #t1 t1
left join #t2 t2
on t1.c1=t2.c1
where t2.c1 is null
),tmp2 as (
select 0 n,*
from tmp1
union all
select n+1 n,t1.c1,t1.c2,t1.c3,t1.r
from tmp2 t1
join tmp1 t2
on t1.c1=t2.c1
and t2.r=t1.r+1
where n<10
)
MERGE #t2
USING tmp2
ON #t2.c1 = tmp2.c1
WHEN NOT MATCHED THEN
INSERT VALUES (tmp2.c1, tmp2.c2, tmp2.c3);
SELECT @@ROWCOUNT;
-- 103
编辑:
感谢 Bartosz Ratajczyk 检查此案例:
原来是和lazy/eager table/index假脱机有关。至少还有两种方法可以强制SQL服务器生成不同的执行计划:
a) 通过使用 TOP (100) PERCENT
DECLARE @n INT = 100;
;with tmp1 as (
select t1.*,
ROW_NUMBER()over(partition by t1.c1 order by(select null))r
from #t1 t1
left join #t2 t2
on t1.c1=t2.c1
where t2.c1 is null
),tmp2 as
(
select 0 n,*
from tmp1
union all
select n+1 n,t1.c1,t1.c2,t1.c3,t1.r
from tmp2 t1
join tmp1 t2
on t1.c1=t2.c1
and t2.r=t1.r+1
where n<10
)
insert #t2
select TOP (@n) PERCENT c1, c2, c3 --,r
from tmp2
SELECT @@ROWCOUNT;
b) 通过使用 ORDER BY .. OFFSET 0 ROWS
:
;with tmp1 as(
select t1.*,
ROW_NUMBER()over(partition by t1.c1 order by(select null))r
from #t1 t1
left join #t2 t2
on t1.c1=t2.c1
where t2.c1 is null
),tmp2 as
(
select 0 n,*
from tmp1
union all
select n+1 n,t1.c1,t1.c2,t1.c3,t1.r
from tmp2 t1
join tmp1 t2
on t1.c1=t2.c1
and t2.r=t1.r+1
where n<10
)
insert #t2
select c1, c2, c3 --,r
from tmp2
ORDER BY 1 OFFSET 0 ROWS;
SELECT @@ROWCOUNT;