web-dev-qa-db-ja.com

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 Server、MySQL、PostgreSQLなど)でこれを機能させる必要があります。そのため、さまざまなデータベースで機能するソリューションが理想的ですが、任意のデータベースで機能するようにする巧妙な方法はありがたいです。

24
cygri

MySQLの場合、貧しい人の generate_series を使用します。これはビューを介して行われます。 MySQLは big four の中で唯一のCTE機能を持たないRDBMSです。

実際に、ビューをサポートするデータベースでこの手法を使用できます。それが事実上すべてのデータベースです

ここにあるジェネレーターテクニック: http://use-the-index-luke.com/blog/2011-07-30/mysql-row-generator#mysql_generator_code

行ったマイナーな変更は、ビット単位(shift leftbitwise or)それぞれ単に乗算と加算を行う元の手法からの手法。 SQL ServerおよびOracleには左シフト演算子がないためです。

この抽象化は、Oracleを除くすべてのデータベースで動作することが99%保証されています。 OracleのSELECTはテーブルなしでは機能しません。これを行うには、ダミーテーブルから選択する必要があります。Oracleはすでに提供しており、DUALテーブルと呼ばれます。データベースの移植性は夢のようなものです:-)

以下は、すべてのRDBMSで機能する抽象化されたビューです。ビット単位の操作(このシナリオではとにかく実際には必要ありません)と機能のニュアンス(OR REPLACE オン CREATE VIEW、すべての主要なデータベースの中で、PostgresqlとMySQLのみがそれらをサポートしています。

Oracle警告:単にFROM DUALSELECT式の後

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

Oracle: http://www.sqlfiddle.com/#!4/26c05/1

SQL Server: http://www.sqlfiddle.com/#!6/84bee/1

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

21
Michael Buen

数値表を使用できます

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

MSSQLを使用したSQLFiddleです

32
Justin Pihony

MySQLは実際にデータベースの世界のIEであり、標準と機能に関しては非常に困難です。

MySQLを除くすべての主要なRDBMSで動作します。

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 Server: http://www.sqlfiddle.com/#!6/911a9/1

Oracle: http://www.sqlfiddle.com/#!4/198cd/1

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

7
Michael Buen

Dbに依存しないソリューションを要求し、@ Justinがニースを提供しました。
あなたも求めました

任意のデータベースで機能させる賢い方法

PostgreSQL: generate_series()には1つあります。

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

ところで、列名としてvaluecountを使いたくないのです。 予約語 を識別子として使用するのは悪い習慣です。代わりにvalctを使用します。

6

数値テーブルを作成します-その定義はプラットフォームによって若干異なる場合があります(これは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全体で有効な結合構文を示しています(ただし、テストはできないので使用しないことを認めます)。

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 Server):

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

感謝のみの場合、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 )
2
HABO

単純なJOINにより、レコードをn回繰り返すという目的に到達できます。
次のクエリは、各レコードを20回繰り返します。

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


master.dbo.spt_values on type = 'P':の注
このテーブルは、type='P'の条件によってハードコーディングされた一連の数値を取得するために使用されます。

1
Siyavash Hamdi

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 [〜#〜]

0
Kaushik Nayak

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
Ebrahim Sabeti