“Поворот” таблицы в SQL (т. е.Перекрестная таблица / crosstabulation Перекрестная таблица)

StackOverflow https://stackoverflow.com/questions/213476

  •  03-07-2019
  •  | 
  •  

Вопрос

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

Campaign
----------
CampaignID

Source
-----------------------
Source_ID | Campaign_ID

Content
---------------------------------------------------------
Content_ID | Campaign_ID | Content_Row_ID | Content_Value

Отчет должен выглядеть следующим образом:

CampaignID - SourceID - ContentRowID(Value(A)) - ContentRowID(Value(B))

Где ContentRowID(значение (A)) означает "Найдите строку с заданным CampaignID и ContentRowId "A", а затем получите ContentValue для этой строки"

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

Это база данных Oracle 10g...

Есть какие-нибудь предложения?

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

Решение

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

Во-первых, вам нужна временная таблица:

CREATE TABLE pivot (count integer);
INSERT INTO pivot VALUES (1);
INSERT INTO pivot VALUES (2);

Теперь мы готовы к запросу.

SELECT campaignid, sourceid, a.contentvalue, b.contentvalue
FROM content a, content b, pivot, source
WHERE source.campaignid = content.campaignid
AND pivot = 1 AND a.contentrowid = 'A'
AND pivot = 2 AND b.contentrowid = 'B'

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

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

SQL не выполняет то, о чем вы просите, поэтому любое "решение", которое вы получите, будет бесполезным.

Если вы знать, конечно, он всегда будет работать на Oracle 10, тогда, конечно, перекрестная таблица Уолтера Митти могла бы это сделать.Правильный способ сделать это - использовать самую простую комбинацию порядка сортировки в запросе и коде приложения, чтобы правильно расположить его.

  • Он работает в других системах баз данных,
  • это не приводит к потере каких-либо других слоев (например, я помню, что у MySQL была проблема с > 255 столбцами.Вы уверены, что вам библиотека интерфейсов справляется так же хорошо, как и сама БД?)
  • это (обычно) не намного сложнее.

Если вам нужно, вы можете просто попросить Content_Row_IDсначала s, затем запросите все нужные вам строки, упорядоченные по CampaignID, ContentRowID, который предоставил бы вам каждую (заполненную) ячейку слева направо, построчно.


Ps.

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

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

SELECT CampaignID, SourceID, 
   (SELECT Content_Value FROM Content c 
      WHERE c.Campaign_ID=s.Campaign_ID 
      AND Content_Row_ID = 39100 
      AND rownum<=1) AS Value39100,
   (SELECT Content_Value FROM Content c 
      WHERE c.Campaign_ID=s.Campaign_ID 
      AND Content_Row_ID = 39200 
      AND rownum<=1) AS Value39200
FROM Source s;

Повторите подзапрос для каждого дополнительного Content_Row_ID.

Чтобы сделать это в стандартном SQL, вам нужно знать все отдельные значения Content_Row_ID и выполнять соединение для каждого отдельного значения. Затем вам нужен столбец для каждого отдельного значения Content_Row_ID.

SELECT CA.Campaign_ID, 
  C1.Content_Value AS "39100",
  C2.Content_Value AS "39200",
  C3.Content_Value AS "39300"
FROM Campaign CA
  LEFT OUTER JOIN Content C1 ON (CA.Campaign_ID = C1.Campaign_ID 
    AND C1.Content_Row_ID = 39100)
  LEFT OUTER JOIN Content C2 ON (CA.Campaign_ID = C2.Campaign_ID 
    AND C2.Content_Row_ID = 39200)
  LEFT OUTER JOIN Content C3 ON (CA.Campaign_ID = C3.Campaign_ID 
    AND C3.Content_Row_ID = 39300);

По мере того, как количество различных значений увеличивается, этот запрос становится слишком дорогим для эффективного выполнения. Возможно, проще получить данные проще и переформатировать их в PL / SQL или в коде приложения.

Билл Карвин и Андерс Эурениус правы в том, что не существует простого решения, а также не существует никакого решения, если число результирующих значений столбцов заранее неизвестно. Oracle 11g несколько упрощает это с помощью оператор PIVOT , но столбцы должны быть известны заранее, и это не соответствует критериям 10g вашего вопроса.

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

http://www.sqlsnippets.com/en/topic-12200.html

http: //asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:124812348063#41097616566309

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

Еще один термин, обозначающий "поворот стола". это кросс-табуляция.

Одним из самых простых инструментов для кросс-таблицы является MS Access. Если у вас есть MS Access, и вы можете установить связь таблицы из базы данных Access с вашей исходной таблицей, вы уже на полпути.

В этот момент вы можете запустить «Мастер запросов» и попросить его создать кросс-таблицу для вас. Это действительно так же просто, как отвечать на вопросы, которые задает вам волшебник. Неудачная сторона этого решения заключается в том, что, если взглянуть на результирующий запрос в представлении SQL, вы увидите некоторый SQL, свойственный диалекту Access SQL, и, в общем, его нельзя использовать на других платформах.

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

Еще раз, если вы действительно хотите сделать это в SQL, " Oracle, полная ссылка " должен помочь вам.

Если вы заранее не знаете количество столбцов, просто верните обычный SQL-запрос и используйте код на стороне сервера, как я перечислил здесь: Заполнение Datagrid и Sql-запроса

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

Удачи

Select DS.Cla,
Sum(case
when (Extract(year from DS.Data) =:intYear) then DS.PRE
else 0
end) as ToTal,
Sum(case
when (Extract(month from DS.Data) =1) then DS.PRE
else 0
end) as Jan,
Sum(case
when (Extract(month from DS.Data) =2) then DS.PRE
else 0
end) as FEV,
Sum(case
when (Extract(month from DS.Data) =3) then DS.PRE
else 0
end) as MAR,
Sum(case
when (Extract(month from DS.Data) =4) then DS.PRE
else 0
end) as ABR,
Sum(case
when (Extract(month from DS.Data) =5) then DS.PRE
else 0
end) as MAI,
Sum(case
when (Extract(month from DS.Data) =6) then DS.PRE
else 0
end) as JUN,
Sum(case
when (Extract(month from DS.Data) =7) then DS.PRE
else 0
end) as JUL,
Sum(case
when (Extract(month from DS.Data) =8) then DS.PRE
else 0
end) as AGO,
Sum(case
when (Extract(month from DS.Data) =9) then DS.PRE
else 0
end) as SETE,
Sum(case
when (Extract(month from DS.Data) =10) then DS.PRE
else 0
end) as OUT,
Sum(case
when (Extract(month from DS.Data) =11) then DS.PRE
else 0
end) as NOV,
Sum(case
when (Extract(month from DS.Data) =12) then DS.PRE
else 0
end) as DEZ
from Dados DS
Where DS.Cla > 0
And Extract(Year from DS.Data) = :intYear
group by DS.CLA

Union All

Select 0*count(DS.cla),  0*count(DS.cla),
Sum(case
when (Extract(month from DS.Data) =1) then DS.PRE
else 0
end) as JAN,
Sum(case
when (Extract(month from DS.Data) =2) then DS.PRE
else 0
end) as FEV,
Sum(case
when (Extract(month from DS.Data) =3) then DS.PRE
else 0
end) as MAR,
Sum(case
when (Extract(month from DS.Data) =4) then DS.PRE
else 0
end) as ABR,
Sum(case
when (Extract(month from DS.Data) =5) then DS.PRE
else 0
end) as MAI,
Sum(case
when (Extract(month from DS.Data) =6) then DS.PRE
else 0
end) as JUN,
Sum(case
when (Extract(month from DS.Data) =7) then DS.PRE
else 0
end) as JUL,
Sum(case
when (Extract(month from DS.Data) =8) then DS.PRE
else 0
end) as AGO,
Sum(case
when (Extract(month from DS.Data) =9) then DS.PRE
else 0
end) as SETE,
Sum(case
when (Extract(month from DS.Data) =10) then DS.PRE
else 0
end) as OUT,
Sum(case
when (Extract(month from DS.Data) =11) then DS.PRE
else 0
end) as NOV,
Sum(case
when (Extract(month from DS.Data) =12) then DS.PRE
else 0
end) as DEZ
from Dados DS
Where DS.Cla > 0
And Extract(Year from DS.Data) = :intYear
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top