sql >> Databasteknik >  >> RDS >> Sqlserver

SQL Server - Välj kolumner som uppfyller vissa villkor?

Jag har skapat en lagrad procedur åt dig.

Denna procedur undersöker MSSQL-metan för att bygga en dynamisk SQL-sträng som returnerar ett resultat som innehåller kolumnnamn N och deras värden V , och motsvarande radtangent K från vilket det värdet hämtades, för en specificerad tabell.

När detta utförs lagras resultaten i en global temporär tabell som heter ##ColumnsByValue, som sedan kan frågas direkt.

Skapa GetColumnsByValue lagrad procedur, genom att köra detta skript:

-- =============================================
-- 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

SP:n tar flera ingångar som parametrar, dessa förklaras i följande kod.

Observera också att jag har tillhandahållit en mekanism för att lägga till en "ignoreringslista" som indata:

  • Detta låter dig lista alla kolumnnamn som inte ska inkluderas i resultaten.
  • Du behöver INTE lägga till kolumnen som du använder som nyckel, dvs. row_id från din exempelstruktur.
  • Du MÅSTE inkludera andra kolumner som inte är varchar eftersom dessa kommer att orsaka ett fel (eftersom SP bara gör en varchar jämförelse med alla kolumner som den tittar på).
  • Detta görs via en temporär tabell som du måste skapa/fylla i
  • Ditt exempel på tabellstrukturen antyder att tabellen endast innehåller kolumner av intresse, så det här kanske inte gäller dig.

Jag har inkluderat exempelkod för hur du gör detta (men gör bara detta om du behöver). till):

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');

Nu, för att avfyra proceduren som bygger din tillfälliga resultattabell, använd följande kod (och modifiera efter behov, naturligtvis).

-- 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

Detta lämnar dig med ##ColumnsByValue , där du kan utföra vilken sökning du behöver, t.ex.:

select * from ##ColumnsByValue WHERE v = 'luxury' and k = 5 --some_row_id

Du skulle behöva köra om den lagrade proceduren (och om det är relevant, skapa/ändra ignoreringslistan före den) för varje tabell du vill undersöka.

Ett problem med detta tillvägagångssätt är att nvarchar-längden kan överskridas i ditt fall. Du skulle prob. behöver använda olika datatyper, minska kolumnnamnslängderna etc. Eller dela upp det i understeg och sammanföra resultaten för att få den resultatuppsättning du är ute efter.

En annan oro jag har är att detta är fullständigt överdrivet för just ditt scenario, där ett enstaka script-to-query-fönster ger dig grunden för vad du behöver, sedan kommer lite smart textredigering i t.ex. Notepad++ att ge dig alla vägen dit... och därför kommer detta problem troligen (och ganska rimligt) att avskräcka dig från att göra det på det här sättet! Men det är en bra allmän fråga, och förtjänar därför ett svar för alla som är intresserade av framtiden;-)




  1. Är din databas säker? Tänk om

  2. SQL VÄLJ MIN

  3. Använder SELECT UNION och returnerar utdata från två kolumner från en tabell

  4. oracle sql-datum senast idag