2012-05-03 94 views
23

我有這樣的結果的SQL查詢:SQL:重複一個結果行多次,並且數行

value | count 
------+------ 
foo |  1 
bar |  3 
baz |  2 

現在我想擴大這個使每一行與count大於1發生多次。我還需要對這些行進行編號。所以,我會得到:

value | count | index 
------+-------+------ 
foo |  1 |  1 
bar |  3 |  1 
bar |  3 |  2 
bar |  3 |  3 
baz |  2 |  1 
baz |  2 |  2 

我必須讓所有主要的數據庫(Oracle,SQL服務器,MySQL和PostgreSQL,也許更多)這項工作。因此,一個適用於不同數據庫的解決方案將是理想的,但可以使用巧妙的方法使其在任何數據庫上都能正常工作。

回答

20

對於MySQL,使用窮人的generate_series,這是通過視圖完成的。 MySQL是big four中唯一沒有任何CTE功能的RDBMS。

實際上,您可以在支持視圖的數據庫上使用此技術。這就是幾乎所有的數據庫

發生器技術在這裏來源:http://use-the-index-luke.com/blog/2011-07-30/mysql-row-generator#mysql_generator_code

我們所做的只有輕微的修改是我們替換按位(左移按位或)從原來的技術與單純乘法技術和分別增加;因爲Sql Server和Oracle沒有左移操作符。

這個抽象是99%保證工作在所有數據庫上,除了Oracle; Oracle的SELECT不能沒有任何表的功能,爲了做到這一點,需要從虛擬表中選擇,Oracle提供了一個,它被稱爲DUAL表。數據庫可移植性是一個白日夢:-)

下面是對所有RDBMS作品抽象的觀點,沒有位操作(這無論如何是不是一個真正的必要性在這種情況下)和功能的細微差別(除去OR REPLACECREATE VIEW,只Postgresql和MySQL支持它們)在所有主要數據庫中。

甲骨文警告:只要把FROM DUAL每個SELECT表達後

CREATE VIEW generator_16 
AS SELECT 0 n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL 
    SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL 
    SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL 
    SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL 
    SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL 
    SELECT 15; 

CREATE VIEW generator_256 
AS SELECT ((hi.n * 16) + lo.n) AS n 
    FROM generator_16 lo, generator_16 hi; 

CREATE VIEW generator_4k 
AS SELECT ((hi.n * 256) + lo.n) AS n 
    FROM generator_256 lo, generator_16 hi; 

CREATE VIEW generator_64k 
AS SELECT ((hi.n * 256) + lo.n) AS n 
    FROM generator_256 lo, generator_256 hi; 

CREATE VIEW generator_1m 
AS SELECT ((hi.n * 65536) + lo.n) AS n 
    FROM generator_64k lo, generator_16 hi; 

然後用這個查詢:

SELECT t.value, t.cnt, i.n 
FROM tbl t 
JOIN generator_64k i 
ON i.n between 1 and t.cnt 
order by t.value, i.n 

PostgreSQL的:http://www.sqlfiddle.com/#!1/1541d/1

甲骨文:http://www.sqlfiddle.com/#!4/26c05/1

SQL服務器:http://www.sqlfiddle.com/#!6/84bee/1

的MySQL:http://www.sqlfiddle.com/#!2/78f5b/1

+0

我最喜歡這個答案,因爲它很便攜。 – cygri

+0

這個答案幫助我解決了我們查詢中的一個主要問題,並幫助我使用此代碼構建了一個全新的報告範圍。希望我能做的不僅僅是投票回答這個問題。 :) – Neels

30

你可以使用一個數字表

SELECT value, count, number 
FROM table 
    JOIN Numbers 
     ON table.count >= Numbers.number 

Here is a SQLFiddle using MSSQL

+1

+1這是最好的和最明確的答案! –

+0

任何洞察這爲什麼這項工程? 4年後,順利通過我的解決方案工作。 – nzaleski

+0

最簡單的解釋是,不是加入1行,而是加入所有行直到計數後的數字。即。計數= 3,則匹配1,2,3創建3 | 1,3 | 2和3 | 3的結果 –

4

創建一個數字表 - 它的定義可能因平臺略有不同(這是SQL Server):

CREATE TABLE Numbers(Number INT PRIMARY KEY); 

INSERT Numbers 
SELECT TOP 1000 ROW_NUMBER() OVER (ORDER BY name) 
FROM sys.all_columns; 

現在這個temp也是SQL Server,但是演示了在你指定的RDBMS中應該有效的連接語法(雖然我會承認我不使用它們,所以我c an't測試):

DECLARE @foo TABLE(value VARCHAR(32), [count] INT); 

INSERT @foo SELECT 'foo', 1 
UNION ALL SELECT 'bar', 3 
UNION ALL SELECT 'baz', 2; 

SELECT f.value, f.[count], [index] = n.Number 
FROM @foo AS f, Numbers AS n 
WHERE n.Number <= f.[count]; 

結果(再次,SQL服務器):

value | count | index 
------+-------+------ 
foo |  1 |  1 
bar |  3 |  1 
bar |  3 |  2 
bar |  3 |  3 
baz |  2 |  1 
baz |  2 |  2 
7

MySQL是真的數據庫世界的IE瀏覽器,它的這種抵抗,當涉及到的標準和特點。

with 
-- Please add this on Postgresql: 
-- RECURSIVE 
tbl_populate(value, cnt, ndx) as 
(
    select value, cnt, 1 from tbl 

    union all 

    select t.value, t.cnt, tp.ndx + 1 
    from tbl t 
    join tbl_populate tp 
    on tp.value = t.value 
    and tp.ndx + 1 <= t.cnt 
) 
select * from tbl_populate 
order by cnt, ndx 

SQL服務器:http://www.sqlfiddle.com/#!6/911a9/1

甲骨文:http://www.sqlfiddle.com/#!4/198cd/1

PostgreSQL的:

上除了MySQL的所有主要RDBMS

作品http://www.sqlfiddle.com/#!1/0b03d/1

+0

+1使用Numbers表的經典解決方案的不錯替代方案。 –

+0

不錯,它的作品,雖然我不太明白它在做什麼:-) – cygri

+0

Heheh真的嗎?現在我受到啓發來做一個關於CTE遞歸的博客。但基本上,行生成器是最簡單的CTE遞歸,它遵循尾遞歸的邏輯;尾遞歸幾乎與循環具有一一對應關係。對於「*數據庫世界的IE」*而言,CTE上的分層遞歸和對象圖遞歸是難以理解的 –

2

升值只,SQL Server 2005和更高可以遞歸處理:

declare @Stuff as Table (Name VarChar(10), Number Int) 
insert into @Stuff (Name, Number) values ('foo', 1), ('bar', 3), ('baz', 2) 

select * from @Stuff 

; with Repeat (Name, Number, Counter) as (
    select Name, Number, 1 
    from @Stuff 
    where Number > 0 
    union all 
    select Name, Number, Counter + 1 
    from Repeat 
    where Counter < Number 
) 
select * 
    from Repeat 
    order by Name, Counter -- Group by name. 
    option (maxrecursion 0) 
6

你問了一個數據庫不可知的解決方案,@Justin給了你一個不錯的解決方案。
你還問了

巧妙的方法來使它在任何數據庫上運行

有一個PostgreSQL的:generate_series()做你問什麼開箱:

SELECT val, ct, generate_series(1, ct) AS index 
FROM tbl; 

順便說一句,我寧願不使用valuecount作爲列名。使用reserved words作爲標識符是不好的做法。改爲使用valct

+0

太棒了!我希望這在任何地方都能奏效是的,你說的保留字是正確的,謝謝指出。 – cygri

1

通過簡單JOIN可以達到重複記錄n倍的目標。
以下查詢重複每個記錄20次。

master.dbo.spt_values on type = 'P'
SELECT TableName.* 
FROM TableName 
JOIN master.dbo.spt_values on type = 'P' and number < 20 


注:
此表用於獲得一個序列號的這是由type='P'條件有硬編碼。

0

可以使用CTE的:

WITH Numbers(Num) AS 
(
    SELECT 1 AS Num 
    UNION ALL 
    SELECT Num + 1 
    FROM Numbers c 
    WHERE c.Num < 1000 
) 

SELECT VALUE,COUNT, number 
FROM TABLE 
     JOIN Numbers 
      ON TABLE.count >= Numbers.Num 
OPTION(MAXRECURSION 1000) 
0

在Oracle中,我們可以使用的LEVELCROSS JOIN的組合。

SELECT * 
    FROM yourtable 
     CROSS JOIN ( SELECT ROWNUM index_t 
          FROM DUAL 
        CONNECT BY LEVEL <= (SELECT MAX (count_t) FROM yourtable)) 
    WHERE index_t <= count_t 
ORDER BY VALUE, index_t; 

DEMO