25

我有一个 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 甚至更多)上进行这项工作。因此,适用于不同数据库的解决方案将是理想的,但值得赞赏的是使其适用于任何数据库的巧妙方法。

4

9 回答 9

32

你可以使用数字表

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

这是一个使用 MSSQL 的 SQLFiddle

于 2012-05-03T01:01:39.887 回答
25

对于 MySQL,使用穷人的generate_series,这是通过视图完成的。MySQL 是四大中唯一没有任何 CTE 功能的 RDBMS。

实际上,您可以在支持视图的数据库上使用此技术。这就是几乎所有的数据库

源自此处的生成器技术:http: //use-the-index-luke.com/blog/2011-07-30/mysql-row-generator#mysql_generator_code

我们所做的唯一小的修改是我们分别用乘法和加法替换了原始技术中的按位(左移按位或)技术;因为 Sql Server 和 Oracle 没有左移运算符。

这种抽象 99% 保证适用于除 Oracle 之外的所有数据库;OracleSELECT没有任何表是无法运行的,为此,需要从 dummy table 中进行选择,Oracle 已经提供了一张,它被称为DUALtable。数据库可移植性是一个白日梦:-)

这是适用于所有 RDBMS 的抽象视图,在所有主要数据库中没有按位操作(在这种情况下无论如何都不是必需的)和功能细微差别(我们删除OR REPLACEon 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

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

Sql 服务器:http ://www.sqlfiddle.com/#!6/84bee/1

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

于 2012-05-03T13:09:37.367 回答
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 服务器:http ://www.sqlfiddle.com/#!6/911a9/1

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

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

于 2012-05-03T02:18:23.130 回答
7

您要求提供与 db 无关的解决方案,@Justin 给了您一个不错的解决方案。
你还要求

使其适用于任何数据库的巧妙方法

PostgreSQLgenerate_series()有一个:开箱即用地满足您的要求:

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

顺便说一句,我宁愿不使用valueandcount作为列名。使用保留字作为标识符是不好的做法。使用valandct代替。

于 2012-05-03T08:07:32.030 回答
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
于 2012-05-03T01:06:24.460 回答
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 )
于 2012-05-03T02:31:11.733 回答
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'

于 2017-01-22T10:42:08.153 回答
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;

演示

于 2018-02-06T13:39:51.663 回答
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)
于 2018-01-13T11:46:47.950 回答