Вопрос

У меня SQL-запрос (MSSQLSERVER), в котором я добавляю столбцы в набор результатов с помощью подвыборов:

SELECT P.name, 
(select count(*) from cars C where C.type = 'sports') AS sportscars,
(select count(*) from cars C where C.type = 'family') AS familycars,
(select count(*) from cars C where C.type = 'business') AS businesscars
FROM people P
WHERE P.id = 1;

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

В приведенном выше примере каждая запись в таблице "люди" также есть три дополнительных столбца: «WantSportscar», «WantFamilycar». и "хочет бизнес-кар". Теперь я хочу сделать выборку для каждого дополнительного столбца только в том случае, если соответствующий " хочет ..... " поле в таблице сотрудников установлено на "true". Другими словами, я хочу сделать первый подвыбор, только если для P.wantsSportscar задано значение true для этого конкретного человека. Второй и третий подпункты должны работать аналогичным образом.

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

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

Если человек А хочет спортивный автомобиль и семейный автомобиль, результат будет включать столбцы «имя», «спортивные автомобили». и "семейные автомобили".

Если человек B хочет бизнес-кар, результат будет включать столбцы «имя» и "Businesscar".

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

Это было полезно?

Решение

В вашем случае возможна разметка столбцов 8 , и для этого вам потребуется 8 отдельных запросов (или динамический сборка запроса).

Невозможно изменить макет набора результатов в одном запросе.

Вместо этого вы можете разработать свой запрос следующим образом:

SELECT  P.name, 
        CASE WHEN wantssport = 1 THEN (select count(*) from cars C where C.type = 'sports') ELSE NULL END AS sportscars,
        CASE WHEN wantsfamily = 1 THEN (select count(*) from cars C where C.type = 'family') ELSE NULL END AS familycars,
        CASE WHEN wantsbusiness = 1 THEN (select count(*) from cars C where C.type = 'business') ELSE NULL END AS businesscars
FROM    people P
WHERE   P.id = 1

, который выберет NULL в соответствующем столбце, если человек этого не хочет, и проанализирует эти NULL на стороне клиента.

Обратите внимание, что реляционная модель отвечает на запросы в виде отношений .

В вашем случае отношение выглядит следующим образом: «Этот человек удовлетворяет потребности многих спортивных автомобилей, бизнес-автомобилей и семейных автомобилей».

Реляционная модель всегда отвечает на этот конкретный вопрос четвертичным отношением.

Он не пропускает ни одного из членов отношения: вместо этого он просто устанавливает для них значение NULL , которое является способом SQL , чтобы показать, что член отношение не определено, не применимо и не имеет смысла.

Другие советы

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

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

Вы можете сделать то, что хотите, сначала выбрав значения в виде отдельных строк во временной таблице, а затем выполнив PIVOT для этой таблицы (превратив строки в столбцы).

  

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

Вы не сможете сделать это простым SQL. Я предлагаю вам сделать этот столбец NULL или ZERO.

Если вы хотите динамически расширять запрос при добавлении новых автомобилей, то PIVOTing может вам в чем-то помочь.

Есть три основных принципа, которым вы хотите научиться, чтобы облегчить эту работу. Первый - нормализация данных, второй - GROUP BY, а третий - PIVOT.

Во-первых, нормализация данных. Ваш дизайн таблицы людей не в первой нормальной форме. Столбцы "wantports", "wantfamily", "wantbusiness" quot; действительно повторяющаяся группа, хотя они могут не выглядеть таковыми. Если вы сможете изменить дизайн таблицы, вам будет полезно создать третью таблицу, назовем ее «peoplewant», с двумя ключевыми столбцами: personid и cartype. Я могу подробно рассказать о том, почему этот дизайн будет более гибким и мощным, если хотите, но сейчас я пропущу это.

На GROUP BY. Это позволяет вам получить результат, который суммирует каждую группу в одной строке результата.

SELECT 
    p.name, 
    c.type, 
    c.count(*) as carcount
FROM people p, 
   INNER JOIN peoplewant pw ON p.id = pw.personid 
   INNER JOIN cars c on pw.cartype = c.type
WHERE
   p.id = 1
GROUP BY 
   p.name,
   c.type

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

Наконец, PIVOT. Инструмент PIVOT в вашей СУБД позволяет вам превратить этот результат в форму, в которой для человека есть только одна строка, и для каждого из типов карт, требуемых для этого человека, есть отдельный столбец. Я сам не использовал PIVOT, поэтому позволю кому-нибудь еще отредактировать этот ответ, чтобы привести пример с использованием PIVOT.

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

Только что наткнулся на это сообщение через поиск в Google, так что я понимаю, что немного опоздал на эту вечеринку, но ... уверен, что это действительно возможно сделать ... однако я я бы не советовал делать это таким образом, потому что это обычно считается очень плохой вещью (тм).

Динамический SQL - ваш ответ.

Прежде чем я скажу, как это сделать, я хочу предвосхитить это, динамический SQL - очень опасная вещь, если вы не очищаете свой ввод от приложения.

Поэтому продолжайте с осторожностью:

declare @sqlToExecute nvarchar(max);
declare @includeSportsCars bit;
declare @includeFamilyCars bit;
declare @includeBusinessCars bit;

set @includeBusinessCars = 1
set @includeFamilyCars = 1
set @includeSportsCars  = 1

set @sqlToExecute = 'SELECT P.name '

if @includeSportsCars = 1 
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''sports'') AS sportscars, ';
if @includeFamilyCars = 1
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''family'') AS familycars, ';
if @includeBusinessCars = 1
    set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''business'') AS businesscars '

set @sqlToExecute = @sqlToExecute + ' FROM people P WHERE P.id = 1;';

exec(@sqlToExecute)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top