Ich habe eine gespeicherte Prozedur für Sie erstellt.
Diese Prozedur untersucht das MSSQL-Meta, um eine dynamische SQL-Zeichenfolge zu erstellen, die ein Ergebnis zurückgibt, das die Spaltennamen N
enthält und ihre Werte V
, und den entsprechenden Zeilenschlüssel K
aus der dieser Wert abgerufen wurde, für eine bestimmte Tabelle.
Wenn dies ausgeführt wird, werden die Ergebnisse in einer globalen temporären Tabelle namens ##ColumnsByValue gespeichert, die dann direkt abgefragt werden kann.
Erstellen Sie GetColumnsByValue
gespeicherten Prozedur, indem Sie dieses Skript ausführen:
-- =============================================
-- 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
Der SP nimmt mehrere Eingaben als Parameter, diese werden im folgenden Code erklärt.
Beachten Sie auch, dass ich einen Mechanismus bereitgestellt habe, um eine "Ignorierliste" als Eingabe hinzuzufügen:
- Damit können Sie alle Spaltennamen auflisten, die nicht in den Ergebnissen enthalten sein sollen.
- Sie müssen NICHT die Spalte hinzufügen, die Sie als Schlüssel verwenden, dh die
row_id
aus Ihrer Beispielstruktur. - Sie MÜSSEN andere Spalten einschließen, die nicht
varchar
sind asthese wird einen Fehler verursachen (wie der SP nur einevarchar
Vergleich aller betrachteten Spalten). - Dies geschieht über eine temporäre Tabelle, die Sie erstellen/befüllen müssen
- Ihre Beispieltabellenstruktur legt nahe, dass die Tabelle nur relevante Spalten enthält, daher trifft dies möglicherweise nicht auf Sie zu.
Ich habe Beispielcode dafür beigefügt (aber nur, wenn Sie brauchen zu):
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');
Um nun die Prozedur auszulösen, die Ihre temporäre Ergebnistabelle erstellt, verwenden Sie den folgenden Code (und ändern ihn natürlich nach Bedarf).
-- 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
Damit bleibt ##ColumnsByValue
übrig , auf dem Sie jede gewünschte Suche durchführen können, z. B.:
select * from ##ColumnsByValue WHERE v = 'luxury' and k = 5 --some_row_id
Sie müssten die gespeicherte Prozedur für jede Tabelle, die Sie untersuchen möchten, erneut ausführen (und ggf. vorher die Ignorierlistentabelle erstellen/ändern).
Ein Problem bei diesem Ansatz ist, dass die nvarchar-Länge in Ihrem Fall möglicherweise überschritten wird. Du würdest es versuchen. Sie müssen einen anderen Datentyp verwenden, die Länge der Spaltennamen reduzieren usw. Oder es in Unterschritte aufteilen und die Ergebnisse zusammenführen, um die gewünschte Ergebnismenge zu erhalten.
Eine weitere Sorge, die ich habe, ist, dass dies für Ihr spezielles Szenario ein völliger Overkill ist, in dem ein einmaliges Skript-zu-Abfrage-Fenster Ihnen die Grundlage dessen liefert, was Sie benötigen, und dann eine clevere Textbearbeitung in zB Notepad ++ Ihnen alles bringt Weg dorthin ... und daher wird Sie dieses Problem wahrscheinlich (und ziemlich vernünftig) davon abhalten, es auf diese Weise zu tun! Aber es ist eine gute allgemeine Frage und verdient daher eine Antwort für jeden, der sich für die Zukunft interessiert;-)