我有一个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以及更多数据库)上完成这项工作。因此,在不同数据库中运行的解决方案将是理想的,但是可以通过巧妙的方式使其适用于任何数据库。
答案 0 :(得分:32)
您可以使用数字表
SELECT value, count, number
FROM table
JOIN Numbers
ON table.count >= Numbers.number
答案 1 :(得分:21)
对于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 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
答案 2 :(得分:7)
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
答案 3 :(得分:6)
你问了一个与数据库无关的解决方案,@ Justin给了你一个很好的解决方案 你还要求
使其适用于任何数据库的聪明方法
PostgreSQL有一个:generate_series()
可以满足您的要求:
SELECT val, ct, generate_series(1, ct) AS index
FROM tbl;
顺便说一句,我宁愿不使用value
和count
作为列名。使用reserved words作为标识符是不好的做法。请改用val
和ct
。
答案 4 :(得分: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中应该有效的连接语法(虽然我会承认我不使用它们所以我无法测试):
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
答案 5 :(得分: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 :(得分:1)
通过简单的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'
条件硬编码的数字。
答案 7 :(得分: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)
答案 8 :(得分:0)
在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;