2009-11-18 3 views
0

У меня странный вопрос о SQL.SQL-запрос - преобразование строк таблицы в столбцы

У меня есть 2 таблицы, скажем, продукты (с столбцами pid и pname) и правила (со столбцами rid и rname), и эти две таблицы не имеют ничего общего. Таблица продуктов содержит 100 строк, а таблица правил имеет 16 строк.

Мне нужно написать запрос таким образом, чтобы результат, который я получил, имеет имя pname в качестве первого столбца, а остальные столбцы - это 16 строк правила таблицы. Другими словами, мне нужно преобразовать строки правила таблицы в столбцы результата. Таким образом, в результате я должен иметь 100 строк (все pnames из таблицы продуктов) и 17 столбцов (pname и 16 строк из таблицы правил).

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

+2

какой сервер базы данных используется? mysql, sql server, oracle? – RRUZ

+0

Я использую сервер sql – Gagan

ответ

1

Если вы используете SQL Server 2005+, вы можете использовать команды Pivot и Unpivot для преобразования строк в столбцы и столбцы в строки.

+0

все еще нужно обойти динамическое число столбцов, но да – Shawn

+0

Пожалуйста, объясните, используя пример. Пожалуйста. Мне это действительно нужно. – Gagan

1

Это называется Pivot. Чтобы иметь динамическое количество столбцов, вам, вероятно, придется использовать динамический SQL-запрос на запрос «на лету» из другого запроса и использовать любой метод eval. В зависимости от того, какой язык вы используете, это может быть легко обработано вызывающим кодом. Я бы рекомендовал рефакторинг сделать это по-другому или подготовиться к нескольким часам обучения. Я не могу помочь вам с запросом без дополнительной информации - схемы db и того, что вы используете sql db.

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

ALTER PROCEDURE [dbo].[CMS_GetCollection] 
    @sectionContentTemplateID int 
    ,@count int = null 
AS 
BEGIN 
    SET NOCOUNT ON; 
SET NOCOUNT ON; 

    declare @columns varchar(max) 
    select 
     @columns = COALESCE(@columns + ',[' + cte.name + '/' + a.name + ']', '[' + cte.name + '/' + a.name + ']') 
    from tcmssection_contenttemplate sct 
       inner join tcmssection s on s.sectionid = sct.sectionid 
       inner join tcmscontenttemplate ct on ct.contenttemplateid = sct.contenttemplateid 
       inner join tcmscontenttemplate_element cte on cte.contenttemplateid = ct.contenttemplateid 
       inner join tcmselement e on cte.elementid = e.elementid 
       inner join tcmsattribute a on e.elementid = a.elementid 
    where 
     a.isadmin = 0 
     and sct.sectioncontenttemplateid = @sectionContentTemplateID 

    declare @query varchar(max) 
    set @query = 
    'select ' + case when @count is not null then 
    ' top ' + convert(varchar(10),@count) else '' end + ' 
     [url] 
     ,pageId,pageName,sortOrder 
     ,[date] 
     , ' + @columns + ' 
    from (
     select 
      s.domainpath + p.filename as [url] 
      ,cte.name + ''/'' + a.name as [element.attribute] 
      ,isnull(pva.valuevarchar, isnull(pva.valuetext,'''')) as [value] 
      ,pv.datepublished as [date],isnull(p.sortOrder,0) as sortOrder,p.pageID,p.name as pageName 
      from tcmssection_contenttemplate sct 
      inner join tcmssection s on s.sectionid = sct.sectionid 
      inner join tcmspage p on p.sectioncontenttemplateid = sct.sectioncontenttemplateid 
      inner join tcmscontenttemplate ct on ct.contenttemplateid = sct.contenttemplateid 
      inner join tcmscontenttemplate_element cte on cte.contenttemplateid = ct.contenttemplateid 
      inner join tcmselement e on cte.elementid = e.elementid 
      inner join tCMSPageVersion pv on p.pageID = pv.pageID 
       and pv.pageVersionID = (
        select top 1 pageVersionID 
        from tCMSPageVersion 
        where pageID = p.pageID and datePublished is not null and datecancelled is null 
        order by datePublished desc 
       ) 
      inner join tcmsattribute a on e.elementid = a.elementid 
      left outer join tcmspageversion_element pve on pve.pageversionid = pv.pageversionid 
       and pve.elementid = e.elementid and pve.name = cte.name 
      left outer join tcmspageversion_attribute pva on pva.attributeid = a.attributeid 
       and pve.pageversionelementid = pva.pageversionelementid 
     where 
      p.isDeleted = 0 
      and a.isadmin = 0 
      and sct.sectioncontenttemplateid = ' + convert(varchar(10), @sectionContentTemplateID) + ' 

    ) [data] 
    pivot (
     max([value]) 
     for [element.attribute] 
     in (' + @columns + ') 
    ) as [pivot]  
    order by [date] desc' 

    execute(@query) 

END 
+0

Можете ли вы, пожалуйста, помочь мне с примером, используя ось поворота на аналогичных строках? – Gagan

0

Честно говоря, я должен был иметь дело с этим некоторое время назад для внутренней задачи обработки данных (сделать с максимальной длиной в SQL Роу будучи 24000 байт или около того). Мое решение состояло в том, чтобы использовать тип данных XML, поскольку он позволяет вам выполнять большинство из этих случайных видов преобразований данных с минимальными хлопотами. Проблема с Pivot, как утверждают другие авторы, заключается в том, что Pivots полагаются на Aggregate Functions для достижения успеха - чего я никогда не хотел делать.

+0

Можете ли вы, пожалуйста, помочь мне с примером? – Gagan

1

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

SELECT 
    products.pname, 
    MAX(CASE WHEN rules.rname = 'Rule 0' THEN products.pid * rules.rid) AS pid_r0, 
    MAX(CASE WHEN rules.rname = 'Rule 1' THEN products.pid * rules.rid) AS pid_r1, 
    MAX(CASE WHEN rules.rname = 'Rule 2 'THEN products.pid * rules.rid) AS pid_r2, 
    ... 
FROM products 
CROSS JOIN rules 
GROUP BY products.pname; 
+0

Я пробовал это .... но он отсортировал pnames в алфавитном порядке. Кроме того, у меня есть 100 строк в таблице продуктов, но мне было всего 86. Я не знаю, где ошибка. Пожалуйста помоги. – Gagan

+0

Вы всегда можете добавить ORDER BY для сортировки чего-то еще (например, pid). Если вы теряете записи, я предполагаю, что у вас нет уникальных pnames, и в этом случае вы можете добавить pid в GROUP BY. – lins314159

Смежные вопросы