根据colume中的值将多个记录插入表中

时间:2017-07-01 13:24:10

标签: sql sql-server

我有一张表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语句实现它。

2 个答案:

答案 0 :(得分:3)

侧栏建议关于增加计数的rCTE

我们会在一分钟内解决问题,但这是对未来代码的建议。

看起来很光滑,你应该避免增加计数的递归CTE(rCTE),因为它们实际上比形成良好的While循环慢,并且即使对于小行计数也要使用更多的资源。抱怨没有在这里写一篇关于这个主题的文章,但需要很长的篇幅才能解释/证明原因。如果您有兴趣了解有关rCTE问题的更多信息,那么您可以查看有关该主题的文章。我相信下面的网站现在已经成功了,所以你可以在不成为会员的情况下查看文章。

Hidden RBAR: Counting with Recursive CTE's

(如果你想要一个StackOverflow链接,试试这个。它有一个测试你可以实际运行我的简陋的盒子的结果。SQL, Auxiliary table of numbers)。

这是该文章的性能图表之一。如果你看一下最左边一条线的红色,几乎垂直的冲天,这就是rCTE的性能曲线。这真的很糟糕。

Performance Comparison of rCTEs vs. 3 Other Common Methods

已发布问题的易耗品测试数据

回到手头的问题,这里有一些易于消费的测试数据,其他人可以使用和使用。只是为了展示这些功能,它比原来的要多一些。

--===============================================================================
--      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选项。