Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

SQL Server - Sélectionnez les colonnes qui remplissent certaines conditions ?

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