Подтвердить что ты не робот

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 и, возможно, больше). Таким образом, решение, которое работает в разных базах данных, было бы идеальным, но рекомендуется использовать умные способы заставить его работать с любой базой данных.

4b9b3361

Ответ 1

Для MySQL используйте бедного человека generate_series, который выполняется через представления. MySQL - единственная СУБД среди больших четырех, у которой нет какой-либо функции CTE.

На самом деле вы можете использовать эту технику в базе данных, которая поддерживает представление. Так что практически вся база данных

Используемая здесь техника генератора: http://use-the-index-luke.com/blog/2011-07-30/mysql-row-generator#mysql_generator_code

Единственная незначительная модификация, которую мы сделали, заключается в замене побитовой (shift left и побитовой или) техники по оригинальной методике с простым умножением и добавлением соответственно; поскольку Sql Server и Oracle не имеют оператора сдвига влево.

Эта абстракция гарантирована на 99% для работы во всей базе данных, кроме Oracle; Oracle SELECT не может функционировать без какой-либо таблицы, для этого нужно выбрать из фиктивной таблицы, Oracle предоставил уже одну, она называется таблицей DUAL. Переносимость базы данных - это мечта: -)

Здесь абстрагированные представления, которые работают на всех РСУБД, лишены побитовых операций (что в действительности не является необходимостью в любом случае в этом сценарии) и характерных нюансов (мы удаляем 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: http://www.sqlfiddle.com/#!6/84bee/1

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

Ответ 3

MySQL действительно является IE мира базы данных, это такое удержание, когда дело доходит до стандартов и функций.

Работает на всех основных СУБД, кроме MySQL:

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

Ответ 4

Вы попросили решение db-agnostic, и @Justin дал вам хороший. Вы также попросили

умные способы заставить его работать с любой базой данных

Есть один для PostgreSQL: generate_series() делает то, что вы просили из коробки:

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

Кстати, я бы предпочел не использовать value и count в качестве имен столбцов. Плохая практика использовать зарезервированные слова в качестве идентификаторов. Вместо этого используйте val и ct.

Ответ 5

Создать таблицу чисел - ее определение может немного отличаться в зависимости от платформы (это для 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

Ответ 6

Только для оценки 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 )

Ответ 7

Простым 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'.