2008-10-20 14 views
4

J'ai une table de données, et j'autorise les gens à ajouter des méta-données à cette table. Je leur donne une interface qui leur permet de traiter comme s'ils ajoutaient des colonnes supplémentaires à la table dans laquelle leurs données sont stockées, mais je stocke les données dans une autre table.Quelle requête SQL affichera "colonnes dynamiques"

Data Table 
    DataID 
    Data 

Meta Table 
    DataID 
    MetaName 
    MetaData 

Donc, s'ils voulaient une table qui a stocké les données, la date et un nom, alors j'aurais les données dans le tableau de données, et le mot « Date » dans metaname, et la date à MetaData, et une autre ligne dans la table meta avec "Name" dans le méta-nom et le nom dans les métadonnées.

J'ai maintenant besoin d'une requête qui prend l'information de ces tables et la présente comme si elle provenait d'une seule table avec les deux colonnes supplémentaires "Données" et "Nom" donc pour le client il semblerait qu'il y ait une seule table avec leurs colonnes personnalisées:

MyTable 
    Data 
    Date 
    Name 

Ou, autrement dit, comment puis-je aller d'ici:

Data Table 
    DataID  Data 
    1    Testing! 
    2    Hello, World! 

Meta Table 
    DataID  MetaName   MetaData 
    1    Date    20081020 
    1    Name    adavis 
    2    Date    20081019 
    2    Name    mdavis 

Pour ici:

MyTable 
    Data   Date    Name 
    Testing!  20081020   adavis 
    Hello, World! 20081019   mdavis 

Il y a quelques années lorsque j'ai fait cela en MySQL en utilisant PHP, j'ai fait deux requêtes, la première pour obtenir les méta-données supplémentaires, la seconde pour les rejoindre toutes ensemble. J'espère que les bases de données modernes ont d'autres méthodes pour y faire face.

Lié à l'option 3 de this question.

-Adam

Répondre

2

Vous voulez faire pivoter chacun de vos lignes de paires nom-valeur dans la MyTable ... Essayez cette sql:

DECLARE @Data TABLE (
    DataID  INT IDENTITY(1,1) PRIMARY KEY, 
    Data  VARCHAR(MAX) 
) 

DECLARE @Meta TABLE (
    DataID  INT , 
    MetaName VARCHAR(MAX), 
    MetaData VARCHAR(MAX) 
) 

INSERT INTO @Data 
SELECT 'Data' 

INSERT INTO @Meta 
SELECT 1, 'Date', CAST(GetDate() as VARCHAR(20)) 
UNION 
SELECT 1, 'Name', 'Joe Test' 

SELECT * FROM @Data 

SELECT * FROM @Meta 

SELECT 
    D.DataID, 
    D.Data, 
    MAX(CASE MetaName WHEN 'Date' THEN MetaData ELSE NULL END) as Date, 
    MAX(CASE MetaName WHEN 'Name' THEN MetaData ELSE NULL END) as Name 
FROM 
    @Meta M 
JOIN @Data D  ON M.DataID = D.DataID 
GROUP BY 
    D.DataID, 
    D.Data 
+0

Plus sur CASE le pied de biche de SQL. http://www.4guysfromrolla.com/webtech/102704-1.shtml –

+0

Quelle est la meilleure façon d'interroger le résultat? Exemple: 'SELECT * FROM Table WHERE Name = 'Joe Test' Aurais-je besoin de sauvegarder les premiers résultats et interroger cette table? Ou y a-t-il un moyen d'implémenter un filtre "WHERE" dans le premier passage? – 182764125216

1
SELECT DataTable.Data AS Data, MetaTable.MetaData AS Date, MetaTable.MetaName AS Name 
FROM DataTable, MetaTable 
WHERE DataTable.DataID = MetaTable.DataID 

Vous voulez probablement ajouter une clause supplémentaire (AND Data = 'une valeur') pour retourner les lignes que l'utilisateur souhaite.

0

AFAIK, vous pouvez faites cela du côté serveur uniquement avec une procédure stockée dynamique SQL.

efficacement le code que vous voulez générer dynamiquement:

SELECT [Data Table].* 
    ,[MyTable Date].MetaData 
    ,[MyTable Name].MetaData 
FROM [Data Table] 
LEFT JOIN [MyTable] AS [MyTable Date] 
    ON [MyTable Date].DataID = [Data Table].DataID 
    AND [MyTable Date].MetaName = 'Date' 
LEFT JOIN [MyTable] AS [MyTable Name] 
    ON [MyTable Name].DataID = [Data Table].DataID 
    AND [MyTable Name].MetaName = 'Name' 

et le code est ici pour le faire:

DECLARE @sql AS varchar(max) 
DECLARE @select_list AS varchar(max) 
DECLARE @join_list AS varchar(max) 
DECLARE @CRLF AS varchar(2) 
DECLARE @Tab AS varchar(1) 

SET @CRLF = CHAR(13) + CHAR(10) 
SET @Tab = CHAR(9) 

SELECT @select_list = COALESCE(@select_list, '') 
         + @Tab + ',[MyTable_' + PIVOT_CODE + '].[MetaData]' + @CRLF 
     ,@join_list = COALESCE(@join_list, '') 
         + 'LEFT JOIN [MyTable] AS [MyTable_' + PIVOT_CODE + ']' + @CRLF 
          + @Tab + 'ON [MyTable_' + PIVOT_CODE + '].DataID = [DataTable].DataID' + @CRLF 
          + @Tab + 'AND [MyTable_' + PIVOT_CODE + '].MetaName = ''' + PIVOT_CODE + '''' + @CRLF 
FROM (
    SELECT DISTINCT MetaName AS PIVOT_CODE 
    FROM [MyTable] 
) AS PIVOT_CODES 

SET @sql = 'SELECT [DataTable].*' + @CRLF 
      + @select_list 
      + 'FROM [DataTable]' + @CRLF 
      + @join_list 
PRINT @sql 
--EXEC (@sql) 

Vous pouvez utiliser une technique dynamique similaire en utilisant l'exemple de déclaration CASE pour effectuer la pivot.

+0

C'est donc l'option de deux requêtes, où je d'abord trouver les colonnes « dynamiques » Je dois tirer à partir de la table de méta, puis construire une requête à partir de ces résultats? –

+0

Oui, cependant, vous pouvez le faire dynamiquement dans un seul proc stocké. Si vos métaNames sont corrigés, vous pouvez utiliser la requête ci-dessus telle quelle, mais sinon, elle sera dynamique. Je posterai du code sous peu ce qui sera vite passé. –

+0

Merci, ce serait utile. Souhaitez-vous également mentionner quel serveur SQL vous savez sur lequel travailler? –