Как выбрать минимальный набор строк, чтобы охватить все возможные значения каждого столбца в SQL? - PullRequest
0 голосов
/ 07 июня 2019

Я выполняю SQL-запрос, чтобы получить данные из таблицы для сопоставления всех возможных значений всех категорий, представленных в каждом столбце.

Как выполнить запрос SELECT, чтобы он возвращал минимальное количество строк, достаточное для включения всех возможных значений всех столбцов?

Например, если у меня есть таблица из 10 строки 3 столбца, каждый столбец содержит 3 возможных значения:

TABLE sales
--------------------------------
   brandID   color       size
--------------------------------
    2        red         big
    3        blue        big
    2        blue        big
    2        red         small
    2        blue        medium
    3        green       small
    3        red         big
    1        green       medium
    2        red         medium
    2        blue        big

Конечно, я мог бы ВЫБРАТЬ все строки из таблицы без фильтра, но это был бы дорогой запрос из 10 строк.

ОднакоКак вы можете видеть, если мы отфильтруем запрос SELECT, чтобы вернуть только следующие строки ниже, можно охватить все возможные значения всех столбцов:

1,2,3 для brandID

красный, синий, зеленый для цвета

большой, маленький, средний для размера

--------------------------------
   brandID   color       size
--------------------------------
    3        blue        big
    2        red         small
    1        green       medium

Как это сделать в запросе SQL?

Ответы [ 5 ]

3 голосов
/ 07 июня 2019

Этот делает то, что вы ожидаете:

select b.brandid, c.color, s.size
from (
  select brandid, row_number() over (order by brandid) as rn
  from sales
  group by brandid
) b
  full join (
    select color, row_number() over (order by color) as rn
    from sales
    group by color
  ) c on b.rn = c.rn
  full join (
    select size, row_number() over (order by size) as rn
    from sales
    group by size
  ) s on b.rn = s.rn;

Онлайн пример: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=e72e7d1dfed43825025c5703b5d3671a


Но это работает правильно, только если у вас одинаковое количество (различных)бренды, цвета и размеры.Если у вас есть, например, 5 брендов, 6 цветов и 7 размеров, результат довольно «странный»:

https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=4417a4d97ecf7601364f09d65f6522fa

2 голосов
/ 07 июня 2019

Это слишком долго для комментария.

Во-первых, запрос, который возвращает десять строк, не "дорогой".

Во-вторых, это очень сложная проблема.Он включает просмотр всех комбинаций строк, чтобы увидеть, есть ли в наборе все комбинации столбцов.Я подозреваю, что любой алгоритм должен будет в основном искать во всех возможных комбинациях - хотя могут быть некоторые преимущества, такие как автоматическое включение всех строк с уникальным значением в любом столбце.

Как сложная проблема, связанная со сравнением миллиардовиз множеств, SQL на самом деле не является подходящим языком для решения этой проблемы.

1 голос
/ 07 июня 2019

Это довольно странное требование ... Но вы можете попробовать кое-что из этого:

DECLARE @sales TABLE(BrandID INT, color VARCHAR(10),size VARCHAR(10));
INSERT INTO @sales VALUES
(2,'red', 'big'),
(3,'blue', 'big'),
(2,'blue', 'big'),
(2,'red', 'small'),
(2,'blue', 'medium'),
(3,'green', 'small'),
(3,'red', 'big'),
(1,'green', 'medium'),
(2,'red', 'medium'),
(2,'blue', 'big');

WITH AllBrands AS (SELECT ROW_NUMBER() OVER(ORDER BY BrandID) AS RowInx, BrandID FROM @sales GROUP BY BrandID)
    ,AllColors AS (SELECT ROW_NUMBER() OVER(ORDER BY color) AS RowInx, color FROM @sales GROUP BY color)
    ,AllSizes  AS (SELECT ROW_NUMBER() OVER(ORDER BY size) AS RowInx, size FROM @sales GROUP BY size) 
SELECT COALESCE(b.RowInx,c.RowInx,s.RowInx) AS RowInx
      ,b.BrandID
      ,c.color
      ,s.size
FROM AllBrands b 
FULL OUTER JOIN AllColors c ON COALESCE(b.RowInx,c.RowInx)=c.RowInx
FULL OUTER JOIN AllSizes s  ON COALESCE(b.RowInx,c.RowInx,s.RowInx)=s.RowInx;

Это решение аналогично @ a_horse_with_no_name's, но позволяет избежать пропусков в результате в случае неравного количества значенийна столбец.

Идея вкратце:

Мы создаем пронумерованный набор всех отдельных значений в столбце и объединяем все наборы на этом числе.Поскольку мы не знаем заранее, я использую COALESCE, чтобы выбрать первое значение, которое не равно нулю.

0 голосов
/ 07 июня 2019

Я думаю, что ваш пример сбивает с толку, имея ровно 3 значения для каждого поля, что делает запрошенный результат разумным.Но что происходит, когда добавляются еще два бренда или новый цвет?Тогда что вы ожидаете получить?

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

  • "Каковы различные бренды?"
  • «Какие цвета?»
  • «Какие размеры?»

Если они должны быть отображены в аккуратной таблице, сшить ихвместе потом на вашем прикладном уровне.Вы могли бы возможно сделать это в SQL с чем-то вроде подсказки a_horse_with_no_name, но на самом деле это не то место.

0 голосов
/ 07 июня 2019

Это не очень хорошая проблема, если вы запрашиваете ОДИН И ТОЛЬКО ОДИН запрос и ОДИН И ТОЛЬКО ОДИН из каждого набора результатов и ОДИН И ТОЛЬКО ОДИН экземпляр каждого результата. Как точно сказал Гордон Линофф: это не проблема для SQL. Я понимаю, что, возможно, у вас НАМНОГО больше таблицы, но он абсолютно прав.

Но добавьте еще один слой, и вы сможете получить именно то, что хотите, со всей необходимой эффективностью и читабельным выводом. Используйте курсор и некоторые базовые SELECT из динамического SQL с SELECT columns.name из sys.tables JOIN sys.columns ON tables.object_id = columns.object_id, если вам абсолютно необходимо сделать это только с помощью TSQL.

И если вы хотите создать базовое приложение с любым фреймворком с драйвером SQL, вы можете просто выбрать SELECT DISTINCT FROM <и поместить различные результаты в массивы. </p>

В качестве альтернативы: перефразируйте ваш вопрос, понимая, что результатом любого SQL-запроса будет x строк на x столбцов. Не массив для каждого столбца.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...