次のような結果の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など)でこれを機能させる必要があります。そのため、さまざまなデータベースで機能するソリューションが理想的ですが、任意のデータベースで機能するようにする巧妙な方法はありがたいです。
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 leftとbitwise or)それぞれ単に乗算と加算を行う元の手法からの手法。 SQL ServerおよびOracleには左シフト演算子がないためです。
この抽象化は、Oracleを除くすべてのデータベースで動作することが99%保証されています。 OracleのSELECT
はテーブルなしでは機能しません。これを行うには、ダミーテーブルから選択する必要があります。Oracleはすでに提供しており、DUAL
テーブルと呼ばれます。データベースの移植性は夢のようなものです:-)
以下は、すべてのRDBMSで機能する抽象化されたビューです。ビット単位の操作(このシナリオではとにかく実際には必要ありません)と機能のニュアンス(OR REPLACE
オン CREATE VIEW
、すべての主要なデータベースの中で、PostgresqlとMySQLのみがそれらをサポートしています。
Oracle警告:単に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
Oracle: http://www.sqlfiddle.com/#!4/26c05/1
SQL Server: http://www.sqlfiddle.com/#!6/84bee/1
数値表を使用できます
SELECT value, count, number
FROM table
JOIN Numbers
ON table.count >= Numbers.number
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
Dbに依存しないソリューションを要求し、@ Justinがニースを提供しました。
あなたも求めました
任意のデータベースで機能させる賢い方法
PostgreSQL: generate_series()
には1つあります。
SELECT val, ct, generate_series(1, ct) AS index
FROM tbl;
ところで、列名としてvalue
とcount
を使いたくないのです。 予約語 を識別子として使用するのは悪い習慣です。代わりにval
とct
を使用します。
数値テーブルを作成します-その定義はプラットフォームによって若干異なる場合があります(これは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
感謝のみの場合、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 )
単純な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'
の条件によってハードコーディングされた一連の数値を取得するために使用されます。
Oracleでは、LEVEL
とCROSS 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;
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)