我有一张表basetable
,如下所示
id name qty price
2 a 2 20
3 d 3 10
4 b 5 60
我想根据数量将记录插入另一个表中,如表2中所示,id 2有两个数量,因此table2将包含2个记录,修改后的数量为1。
我尝试使用while loop&插入语句,它工作,但我想使用select into语句实现它。
答案 0 :(得分:3)
侧栏建议关于增加计数的rCTE
我们会在一分钟内解决问题,但这是对未来代码的建议。
看起来很光滑,你应该避免增加计数的递归CTE(rCTE),因为它们实际上比形成良好的While循环慢,并且即使对于小行计数也要使用更多的资源。抱怨没有在这里写一篇关于这个主题的文章,但需要很长的篇幅才能解释/证明原因。如果您有兴趣了解有关rCTE问题的更多信息,那么您可以查看有关该主题的文章。我相信下面的网站现在已经成功了,所以你可以在不成为会员的情况下查看文章。
Hidden RBAR: Counting with Recursive CTE's
(如果你想要一个StackOverflow链接,试试这个。它有一个测试你可以实际运行我的简陋的盒子的结果。SQL, Auxiliary table of numbers)。
这是该文章的性能图表之一。如果你看一下最左边一条线的红色,几乎垂直的冲天,这就是rCTE的性能曲线。这真的很糟糕。
已发布问题的易耗品测试数据
回到手头的问题,这里有一些易于消费的测试数据,其他人可以使用和使用。只是为了展示这些功能,它比原来的要多一些。
--===============================================================================
-- Create a test table for the original problem.
-- This is NOT a part of the solution.
-- We're just creating a larger set of data to test with.
--===============================================================================
--===== If the test table exists, drop it to make reruns in SSMS easier.
-- We'll use a Temp Table just to prevent accidental drops of a real table.
IF OBJECT_ID('tempdb..#TestTable','U') IS NOT NULL
DROP TABLE #TestTable
;
GO --Just to make table modifications easier if the table already exists.
--===== Create and populate the test table on-the-fly (kept this 2005 compatible)
WITH cteTestData (SomeID,SomeName,Qty,Price) AS
(
SELECT 4,'D',5,60 UNION ALL --Need to make 5 rows.
SELECT 2,'B',4,20 UNION ALL --Need to make 4 rows.
SELECT 5,'F',0,86 UNION ALL --Need to make 0 rows.
SELECT 3,'C',2,10 UNION ALL --Need to make 2 rows.
SELECT 1,'D',2,22 UNION ALL --Need to make 2 rows. Name duplicated.
SELECT 7,'D',1,11 --Need to make 1 row. Name duplicated.
)
SELECT SomeID,SomeName,Qty,Price
INTO #TestTable
FROM cteTestData
;
--===== Display the contents of the table in order by name and quantity
-- just to make verification easy.
SELECT *
FROM #TestTable
ORDER BY SomeName,Qty
;
GO
建立有用的工具 - 最后
我们可以完全内联地解决这个问题,但我们也可以为我们的T-SQL服务器工具箱添加一个非常有用的工具。这是一个简化的Tally Table-like函数,只需从1到参数化最大值。它是一个iTVF(内联表值函数),这意味着,与标量或多语句函数不同,它是快速讨厌的,并且在此过程中产生零逻辑读取(谢谢Ben-Gan先生)。它使用基于CROSS JOIN的cCTE(Cascading CTE)使其快速而不是任何形式的递归。这也意味着您不必在任何地方设置MAXRCURSION选项。你将来会发现这样的功能有很多用途。
CREATE FUNCTION [dbo].[fnTally]
/**********************************************************************************************************************
Purpose:
Return a column of BIGINTs from 1up to and including @MaxN with a max value of 1 Trillion.
As a performance note, it takes about 00:01:45 (hh:mm:ss) to generate 1 Billion numbers to a throw-away variable.
Usage:
--===== Basic syntax example (Returns BIGINT)
SELECT t.N
FROM dbo.fnTally(@MaxN) t
;
Notes:
1. Can also be used with APPLY.
2. Note that this does not contain Recursive CTEs (rCTE), which are much slower and resource intensive.
3. Based on Itzik Ben-Gan's cascading CTE (cCTE) method for creating a "readless" Tally Table source of BIGINTs.
Refer to the following URLs for how it works and introduction for how it replaces certain loops.
http://www.sqlservercentral.com/articles/T-SQL/62867/
http://sqlmag.com/sql-server/virtual-auxiliary-table-numbers
4. As a bit of a sidebar, one of the definitions of the word "Tally" is "to count", which is what this function does.
It "counts" from 1 to whatever the value of @MaxN is every time it's called and it does so without producing any
logical reans and it's nasty fast.
5. I don't normally use the "Hungarian Notation" method of naming but I also have a table name Tally and so this
function needed to be named differently.
-- Jeff Moden
**********************************************************************************************************************/
(@MaxN BIGINT)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN WITH
E1(N) AS (SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL --Could be converted to VALUES after 2008
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL --but there's no performance gain in doing so
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL --so kept it "2005 compatible".
SELECT 1) --10^1 or 10 rows
, E4(N) AS (SELECT 1 FROM E1 a, E1 b, E1 c, E1 d) --10^4 or 10 Thousand rows
,E12(N) AS (SELECT 1 FROM E4 a, E4 b, E4 c) --10^12 or 1 Trillion rows (yeah, call me when that's done. ;-)
SELECT TOP(@MaxN) N = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E12 -- Values from 1 to @MaxN
;
GO
解决方案现在简单,简短,快速讨厌
一旦你有了这样的功能,这类问题(关系乘法)的解决方案变得非常容易且几乎无足轻重。对于使用我在上面提供的测试数据给出的问题,这是一个非常高性能的解决方案。
--===== Solve the original problem
SELECT st.SomeID
,st.SomeName
,Qty = 1
,st.Price --Assuming Price is "for each".
FROM #TestTable st
CROSS APPLY dbo.fnTally(st.Qty)
ORDER BY SomeName,SomeID --Not necessary. Just makes visual verification easy.
;
这是结果集。
SomeID SomeName Qty Price
----------- -------- ----------- -----------
2 B 1 20
2 B 1 20
2 B 1 20
2 B 1 20
3 C 1 10
3 C 1 10
1 D 1 22
1 D 1 22
4 D 1 60
4 D 1 60
4 D 1 60
4 D 1 60
4 D 1 60
7 D 1 11
(14 row(s) affected)
答案 1 :(得分:2)
您可以使用递归CTE生成数据:
with cte as (
select bt.id, bt.name, bt.qty, bt.price, 1 as cnt
from basetable bt
union all
select bt.id, bt.name, bt.qty, bt.price, cnt + 1
from cte
where cnt < bt.qty
)
select id, name, 1 as qty, price
from cte;
如果您想将数据放在另一个表格中,请在insert
之前使用select
,或在into
之后使用select
。
注意:如果您的数量非常大,则可能需要调查MAXRECURSION
选项。