J'ai créé une procédure stockée pour vous.
Cette procédure examine la méta MSSQL pour créer une chaîne SQL dynamique qui renvoie un résultat contenant les noms de colonne N
et leurs valeurs V
, et la clé de ligne correspondante K
à partir de laquelle cette valeur a été récupérée, pour une table spécifiée.
Lorsque cela est exécuté, les résultats sont stockés dans une table temporaire globale appelée ##ColumnsByValue, qui peut ensuite être interrogée directement.
Créez le GetColumnsByValue
procédure stockée, en exécutant ce script :
-- =============================================
-- Author: Ben Roberts ([email protected])
-- Create date: 22 Mar 2013
-- Description: Returns the names of columns that contain the specified value, for a given row
-- =============================================
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF OBJECT_ID ( 'dbo.GetColumnsByValue', 'P' ) IS NOT NULL
DROP PROCEDURE dbo.GetColumnsByValue;
GO
CREATE PROCEDURE dbo.GetColumnsByValue
-- Add the parameters for the stored procedure here
@idColumn sysname,
@valueToFind nvarchar(255),
@dbName sysname,
@tableName sysname,
@schemaName sysname,
@debugMode int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE @SQL nvarchar(max);
DECLARE @SQLUnion nvarchar(max);
DECLARE @colName sysname;
DECLARE @dbContext nvarchar(256);
DECLARE @Union nvarchar(10);
SELECT @dbContext = @dbName + '.' + @schemaName + '.sp_executeSQL';
SELECT @SQLUnion = '';
SELECT @Union = '';
IF OBJECT_ID ( 'tempdb..##GetColumnsByValueIgnoreList') IS NULL -- no columns to ingore have been specified, need to create an empty list.
BEGIN
CREATE TABLE ##GetColumnsByValueIgnoreList (column_name nvarchar(255));
END
DECLARE DBcursor CURSOR FOR
SELECT
COLUMN_NAME
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = @tableName
AND
TABLE_SCHEMA = @schemaName;
OPEN DBcursor;
FETCH DBcursor INTO @colName;
WHILE (@@FETCH_STATUS = 0)
BEGIN
IF (
@colName != @idColumn
AND
@colName NOT IN (SELECT column_name FROM ##GetColumnsByValueIgnoreList)
)
BEGIN
SELECT @SQL = 'SELECT '[email protected]+' as K, '''[email protected]+''' as N, ' [email protected]+ ' as V FROM ' + @dbName + '.' + @schemaName + '.' + @tableName;
--PRINT @SQL;
SELECT @SQLUnion = @SQL + @Union + @SQLUnion;
SELECT @Union = ' UNION ';
END
FETCH DBcursor INTO @colName;
END; -- while
CLOSE DBcursor; DEALLOCATE DBcursor;
IF (@debugMode != 0)
BEGIN
PRINT @SQLUnion;
PRINT @dbContext;
END
ELSE
BEGIN
-- Delete the temp table if it has already been created.
IF OBJECT_ID ('tempdb..##ColumnsByValue') IS NOT NULL
BEGIN
DROP TABLE ##ColumnsByValue
END
-- Create a new temp table
CREATE TABLE ##ColumnsByValue (
K nvarchar(255), -- Key
N nvarchar(255), -- Column Name
V nvarchar(255) -- Column Value
)
-- Populate it with the results from our dynamically generated SQL.
INSERT INTO ##ColumnsByValue EXEC @dbContext @SQLUnion;
END
END
GO
Le SP prend plusieurs entrées comme paramètres, celles-ci sont expliquées dans le code suivant.
Notez également que j'ai fourni un mécanisme pour ajouter une "liste ignorée" en entrée :
- Cela vous permet de lister tous les noms de colonne qui ne doivent pas être inclus dans les résultats.
- Vous n'avez PAS besoin d'ajouter la colonne que vous utilisez comme clé, c'est-à-dire le
row_id
à partir de votre exemple de structure. - Vous DEVEZ inclure d'autres colonnes qui ne sont pas
varchar
car ceux-ci provoqueront une erreur (car le SP fait juste unvarchar
comparaison sur toutes les colonnes qu'il examine). - Cela se fait via une table temporaire que vous devez créer/remplir
- Votre structure de tableau d'exemple suggère que le tableau ne contient que des colonnes d'intérêt, donc cela peut ne pas s'appliquer à vous.
J'ai inclus un exemple de code pour savoir comment procéder (mais ne le faites que si vous avez besoin à):
IF OBJECT_ID ( 'tempdb..##GetColumnsByValueIgnoreList') IS NOT NULL
BEGIN
DROP TABLE ##GetColumnsByValueIgnoreList;
END
CREATE TABLE ##GetColumnsByValueIgnoreList (column_name nvarchar(255));
INSERT INTO ##GetColumnsByValueIgnoreList VALUES ('a_column');
INSERT INTO ##GetColumnsByValueIgnoreList VALUES ('another_column');
INSERT INTO ##GetColumnsByValueIgnoreList VALUES ('yet_another_column');
Maintenant, pour lancer la procédure qui crée votre table temporaire de résultats, utilisez le code suivant (et modifiez-le selon les besoins, bien sûr).
-- Build the ##ColumnsByValue table
EXEC dbo.GetColumnsByValue
@idColumn = 'row_id', -- The name of the column that contains your row ID (eg probably your PK column)
@dbName = 'your_db_name',
@tableName = 'your_table_name',
@schemaName = 'dbo',
@debugMode = 0 -- Set this to 1 if you just want a print out of the SQL used to build the temp table, to 0 if you want the temp table populated
Cela vous laisse avec ##ColumnsByValue
, sur lequel vous pouvez effectuer la recherche dont vous avez besoin, par exemple :
select * from ##ColumnsByValue WHERE v = 'luxury' and k = 5 --some_row_id
Vous devrez ré-exécuter la procédure stockée (et, le cas échéant, créer/modifier la table de liste ignorée avant celle-ci) pour chaque table que vous souhaitez examiner.
Un problème avec cette approche est que la longueur de nvarchar pourrait être dépassée dans votre cas. Vous auriez prob. besoin d'utiliser un type de données différent, de réduire la longueur des noms de colonne, etc. Ou de le diviser en sous-étapes et d'unir les résultats ensemble pour obtenir l'ensemble de résultats que vous recherchez.
Une autre préoccupation que j'ai est que c'est complètement exagéré pour votre scénario particulier, où une fenêtre de script à requête unique vous donnera la base de ce dont vous avez besoin, puis une édition de texte intelligente dans, par exemple, Notepad ++ vous donnera tout le chemin là-bas ... et donc ce problème vous dissuadera probablement (et tout à fait raisonnablement) de le faire de cette façon! Mais c'est une bonne question générale, et mérite donc une réponse pour quiconque s'intéresse à l'avenir;-)