2010-02-05 2 views
2

je dois sélectionner colums statique + un nombre dynamique de lignes sous forme de colonnes dans SQLSQL: Sélectionnez un nombre dynamique de lignes sous forme de colonnes

TABLE 1 
------- 
HotelID 
BlockID 
BlockName 

TABLE 2 
------- 
BlockDate (unknown number of these) 
NumberOfRooms 

Desired Result Row 
------------------ 
HotelID | BlockID | BlockName | 02/10/10 | 02/11/10 | 02/12/10 | ...N 

Lorsque les colonnes de date sont le nombre inconnu de lignes BlockDate.

+2

Est-ce que Tableau2 ont une BlockDate par ligne? Quel est le lien entre Table1 et Table2? Vous cherchez probablement un pivot. – DyingCactus

Répondre

0

Il vous manque une clé étrangère. Je dois supposer que BlockId devrait être PK dans la table 2?

Aussi, en supposant que ce soit un db hérité et en changeant la structure n'est pas une option, je dois demander quelle plate-forme?

Si sq sql, cela pourrait facilement être réalisé en utilisant une instruction SQL dynamique.

1

Qu'est-ce que vous avez besoin est une requête de pivot, pour convertir les données en ligne colonnaire:

SELECT t.hotelid, 
     t.blockid, 
     t.blockname, 
     MAX(CASE WHEN t2.blockdate = '02-10-10' THEN t.numberofrooms ELSE NULL END) AS 02_10_10, 
     MAX(CASE WHEN t2.blockdate = '02-11-10' THEN t.numberofrooms ELSE NULL END) AS 02_11_10, 
     MAX(CASE WHEN t2.blockdate = '02-12-10' THEN t.numberofrooms ELSE NULL END) AS 02_12_10, 
     ... 
    FROM TABLE_1 t 
    JOIN TABLE_2 t2 
GROUP BY t.hotelid, t.blockid, t.blockname 
  1. esprit qu'il n'y a rien à relier les tables - de façon réaliste TABLE_2 besoins hotelid et blockid attributs. Comme-est, cela renvoie les résultats de TABLE_2 pour chaque enregistrement TABLE_1 ...

  2. La base de données est importante, car il aura besoin SQL dynamique pour créer les MAX(CASE... déclarations

1

Vous ferez cela en le client.

SQL est un langage à colonne fixe: vous ne pouvez pas avoir un nombre variable de colonnes (même avec PIVOT, etc.). Le SQL dynamique n'est pas une bonne idée.

+0

Le SQL dynamique est généralement mauvais. D'accord. Cela peut limiter la capacité du serveur à mettre en cache les jeux de résultats, etc. Mais, parfois, il est juste plus rapide de le faire de la "mauvaise" manière. :) – BMiner

0

J'ai déjà écrit une procédure stockée qui faisait juste quelque chose comme ça. Sont donnés une table d'utilisateurs avec des détails de base et un nombre variable de propriétés de profil pour les utilisateurs. (Le nombre de propriétés de profil varie selon le portail DotNetNuke)

Ceci est directement à partir de DotNetNuke 4.9, vérifiez le schéma de la base de données à partir de là et vous obtiendrez les détails. Tables impliquées sont des utilisateurs, UserPortals, UserProfile, ProfilePropertyDefinition

En bref mots:

  1. Je crée une table temporaire avec toutes les propriétés de profil sous forme de colonnes (dynamiquement)
  2. je remplir la table temporaire avec userid (comme clé étrangère pour relier à la table des utilisateurs et toutes les propriétés du profil des données
  3. Je rejoins une table sur les utilisateurs et table temporaire

Cela me donne une ligne pe r utilisateur avec toutes les propriétés de profil.

Voici le code - pas parfait mais bon son sur mesure pour mes besoins mais devrait être assez facile à réutiliser (essayé d'éviter les curseurs btw)

set ANSI_NULLS ON 
set QUOTED_IDENTIFIER ON 
go 

ALTER PROCEDURE [dbo].[rows2cols] @portalId INT 
AS BEGIN 
print 'PortalID=' + convert(varchar,@portalId) 
    --SET NOCOUNT ON; 
    declare @idx int  
    declare @rowcount int 
    declare @tmpStr nvarchar(max) 
    declare @ctype nvarchar(max) 
    declare @cname nvarchar(max) 
    declare @clen int 
    declare @createStr nvarchar(max)  
--------------------------------------------------------------------------- 
-- create tmp table -- 
--------------------------------------------------------------------------- 
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[xxxx]') AND type in (N'U')) 
DROP TABLE [dbo].[xxxx] 

print 'Building Temp Table Cols for profile properties...' 
set @rowcount = (select count(*) from ProfilePropertyDefinition where PortalID=0 and deleted=0) 
set @idx = 1 
set @tmpStr = '' 
while (@idx <= @rowcount) 
begin 
    -- dynamically generate rownumbers to be able to do loop over them (avoid cursors) 
    select @cname = t1.PropertyName from 
    (select ROW_NUMBER() OVER (ORDER BY ViewOrder) as Idx, PropertyName from ProfilePropertyDefinition 
     where PortalID=0 and deleted=0 
    ) as t1 where t1.Idx = @idx 

    if (@cname = 'Email' or @cname = 'FirstName' or @cname = 'LastName') begin 
     set @clen = 1 
    end else begin 
     set @tmpStr = @tmpStr + '[' + @cname + '] [nvarchar](500), ' 
    end 
    set @idx = @idx + 1 
end 

set @tmpStr = @tmpStr + '[userid] [int] ' 
set @createStr = 'create table xxxx (' + @tmpStr + ')' 

Print @createStr 
Exec (@createStr) 

--------------------------------------------------------------------------- 
-- fill tmp table -- 
---------------------------------------------------------------------------  
declare @propName nvarchar(max) 
declare @propVal nvarchar(max) 
declare @userId int 
declare @idx2 int 
declare @rowcount2 int 
declare @inscol nvarchar(max) 
declare @insval nvarchar(max) 

set @rowcount = (select count(*) FROM Users LEFT OUTER JOIN UserPortals ON Users.UserID = UserPortals.UserId WHERE UserPortals.PortalId = @portalId) 
set @idx = 1 
    while (@idx <= @rowcount) 
    begin 
     -- get userId 
     select @userId = t1.UserID from (select u.UserID, ROW_NUMBER() OVER (ORDER BY u.UserID) as Idx 
     from Users as u LEFT OUTER JOIN UserPortals as up ON u.UserID = up.UserId where up.PortalId = @portalId) as t1 
     where t1.Idx = @idx 

     set @idx2 = 1 
     set @rowcount2 = (select count(*) from UserProfile where UserID = @userId) 
     set @inscol = '' 
     set @insval = '' 

     while (@idx2 < @rowcount2) 
     begin 
      -- build insert for a specific user 
      select @propName = t1.PropertyName , @propVal=t1.PropertyValue from 
      (select ROW_NUMBER() OVER (ORDER BY ProfileID) as Idx, up.PropertyDefinitionID,ppd.PropertyName, up.PropertyValue 
       from UserProfile as up 
       inner join ProfilePropertyDefinition as ppd on up.PropertyDefinitionID = ppd.PropertyDefinitionID 
       where UserID = @userId 
      ) as t1 where t1.Idx = @idx2 

      if (@propName != 'Firstname' and @propName != 'LastName' and @propName != 'Email') 
      begin 
       set @inscol = @inscol + @propName + ', ' 
       set @insval = @insval + 'N''' + replace(@propVal,'''','''''') + ''', ' 
      end 

      set @idx2 = @idx2 + 1 
     end 

     set @inscol = @inscol + 'userid' 
     set @insval = @insval + convert(nvarchar,@userId) 

     set @tmpStr = 'insert into xxxx (' + @inscol + ') values (' + @insval + ')' 
     --print @tmpStr 
     Exec(@tmpStr) 
     set @idx = @idx + 1 
    end 

-- ------------------------------------------------------------------------- 
-- return tmp table & dump -- 
-- -------------------------------------------------------------------------  
SELECT Users.*, xxxx.* FROM xxxx INNER JOIN Users ON xxxx.userid = Users.UserID 

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[xxxx]') AND type in (N'U')) 
DROP TABLE [dbo].[xxxx] 
END 
Questions connexes