Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

SQL Server - Spalten auswählen, die bestimmte Bedingungen erfüllen?

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 eine varchar 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;-)