sql >> Base de Datos >  >> RDS >> Sqlserver

SQL Server:¿seleccionar columnas que cumplan ciertas condiciones?

He creado un procedimiento almacenado para ti.

Este procedimiento examina el metadato de MSSQL para crear una cadena SQL dinámica que devuelve un resultado que contiene nombres de columna N y sus valores V , y la tecla de fila correspondiente K de la que se recuperó ese valor, para una tabla específica.

Cuando se ejecuta esto, los resultados se almacenan en una tabla temporal global llamada ##ColumnsByValue, que luego se puede consultar directamente.

Cree el GetColumnsByValue procedimiento almacenado, ejecutando este 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

El SP toma varias entradas como parámetros, estos se explican en el siguiente código.

Tenga en cuenta que también proporcioné un mecanismo para agregar una "lista de ignorados" como entrada:

  • Esto le permite enumerar cualquier nombre de columna que no deba incluirse en los resultados.
  • NO necesita agregar la columna que está usando como clave, es decir, row_id de su estructura de ejemplo.
  • DEBE incluir otras columnas que no sean varchar ya que esto causará un error (ya que el SP solo hace un varchar comparación en todas las columnas que mira).
  • Esto se hace a través de una tabla temporal que debe crear/rellenar
  • Su estructura de tabla de ejemplo sugiere que la tabla contiene solo columnas de interés, por lo que es posible que esto no se aplique a usted.

He incluido un código de ejemplo sobre cómo hacer esto (pero solo hazlo si necesitas a):

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

Ahora, para iniciar el procedimiento que crea su tabla temporal de resultados, use el siguiente código (y modifíquelo según corresponda, por supuesto).

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

Esto te deja con ##ColumnsByValue , en el que puede realizar cualquier búsqueda que necesite, por ejemplo:

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

Deberá volver a ejecutar el procedimiento almacenado (y, si corresponde, crear/modificar la tabla de la lista de ignorados antes) para cada tabla que desee examinar.

Una preocupación con este enfoque es que la longitud de nvarchar podría superarse en su caso. Usted probablemente. necesita usar un tipo de datos diferente, reducir la longitud de los nombres de las columnas, etc. O dividirlo en subpasos y unir los resultados para obtener el conjunto de resultados que busca.

Otra preocupación que tengo es que esto es una completa exageración para su escenario particular, donde una ventana única de script a consulta le dará la base de lo que necesita, luego una edición de texto inteligente en, por ejemplo, Notepad ++ le dará todo el allí... y, por lo tanto, este problema probablemente (y bastante razonablemente) lo disuadirá de hacerlo de esta manera. Pero es una buena pregunta de caso general, por lo que merece una respuesta para cualquier persona interesada en el futuro;-)