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等等)上进行这项工作。 因此,一个在不同数据库中工作的解决scheme将是理想的,但是要使它能够在任何数据库上工作的聪明方法,
对于MySQL,使用穷人的generate_series ,这是通过视图完成的。 MySQL是唯一没有任何CTE特性的四大企业中的RDBMS。
其实你可以在支持view的数据库上使用这个技巧。 所以这几乎是所有的数据库
发电机技术来自这里: http : //use-the-index-luke.com/blog/2011-07-30/mysql-row-generator#mysql_generator_code
我们所做的唯一微小的修改是,我们用原来的技术分别replace了单纯的乘法和加法按位( 左移和逐位 )技术; 因为Sql Server和Oracle没有左移操作符。
这个抽象是99%保证工作在所有的数据库,除了甲骨文; Oracle的SELECT
不能没有任何表的function,为了做到这一点,需要从虚拟表中select,Oracle提供了一个,它被称为DUAL
表。 数据库可移植性是一个梦想:-)
下面是在所有RDBMS上工作的抽象视图,没有按位操作(在这种情况下实际上并不是必须的),并且在所有主要数据库中都有细微差别(我们在CREATE VIEW
上删除OR REPLACE
,只有PostgreSQL和MySQL支持它们)。
Oracle警告:在每个SELECT
expression式之后放置FROM DUAL
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, in FROM tbl t JOIN generator_64k i ON in between 1 and t.cnt order by t.value, in
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
你可以使用数字表
SELECT value, count, number FROM table JOIN Numbers ON table.count >= Numbers.number
这里是一个使用MSSQL的SQLFiddle
MySQL是真正的数据库世界的IE浏览器,当涉及到标准和function时,它是如此坚强。
适用于除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
你问了一个数据库不可知的解决scheme,@Justin给了你一个很好的解决scheme。
你也要求
聪明的方法,使其在任何数据库上工作
有一个用于PostgreSQL: generate_series()
做你开箱的要求:
SELECT val, ct, generate_series(1, ct) AS index FROM tbl;
顺便说一句,我宁愿不value
和列数名称。 使用保留字作为标识符是不好的做法。 改用val
和ct
。
创build一个数字表 – 它的定义可能因平台而异(这是针对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中应该是有效的连接语法(虽然我会承认我不使用它们,所以我不能testing):
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及更高版本可以recursion处理:
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
您可以达到重复loggingn次的目的。
以下查询重复每个logging20次。
SELECT TableName.* FROM TableName JOIN master.dbo.spt_values on type = 'P' and number < 20
master.dbo.spt_values on type = 'P'
注意事项:
该表用于获取一系列在其中按type='P'
条件硬编码的数字。