Stworzyłem dla Ciebie procedurę składowaną.
Ta procedura sprawdza meta MSSQL w celu zbudowania dynamicznego ciągu SQL, który zwraca wynik zawierający nazwy kolumn N
i ich wartości V
i odpowiedni klawisz wiersza K
z którego ta wartość została pobrana, dla określonej tabeli.
Kiedy to jest wykonywane, wyniki są przechowywane w globalnej tabeli tymczasowej o nazwie ##ColumnsByValue, do której można następnie bezpośrednio zapytać.
Utwórz GetColumnsByValue
procedura składowana, wykonując ten skrypt:
-- =============================================
-- 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 przyjmuje kilka danych wejściowych jako parametry, są one wyjaśnione w poniższym kodzie.
Zauważ też, że udostępniłem mechanizm dodawania „listy ignorowanych” jako danych wejściowych:
- Dzięki temu możesz wyświetlić listę nazw kolumn, które nie powinny być uwzględnione w wynikach.
- NIE musisz dodawać kolumny, której używasz jako klucza, tj.
row_id
z przykładowej struktury. - MUSISZ uwzględnić inne kolumny, które nie są
varchar
asthese spowoduje błąd (ponieważ SP po prostu robivarchar
porównanie we wszystkich kolumnach, na które patrzy). - Odbywa się to za pomocą tabeli tymczasowej, którą musisz utworzyć/wypełnić
- Przykładowa struktura tabeli sugeruje, że tabela zawiera tylko interesujące kolumny, więc może to nie dotyczyć Ciebie.
Dołączyłem przykładowy kod, jak to zrobić (ale rób to tylko wtedy, gdy potrzebujesz do):
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');
Teraz, aby uruchomić procedurę, która buduje twoją tymczasową tabelę wyników, użyj poniższego kodu (i oczywiście odpowiednio zmodyfikuj).
-- 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
Pozostaje Ci ##ColumnsByValue
, na którym możesz przeprowadzić dowolne wyszukiwanie, np.:
select * from ##ColumnsByValue WHERE v = 'luxury' and k = 5 --some_row_id
Musisz ponownie wykonać procedurę składowaną (i jeśli to konieczne, utworzyć/zmodyfikować przed nią tabelę listy ignorowanych) dla każdej tabeli, którą chcesz sprawdzić.
Problemem związanym z tym podejściem jest to, że w twoim przypadku może zostać przekroczona długość nvarchar. Zapewne. musisz użyć różnych typów danych, skrócić długości nazw kolumn itp. Lub podziel je na podetapy i połącz wyniki, aby uzyskać zestaw wyników, którego szukasz.
Kolejną obawą, jaką mam, jest to, że jest to kompletna przesada w przypadku twojego konkretnego scenariusza, w którym jednorazowy skrypt do zapytania da ci podstawę tego, czego potrzebujesz, a następnie sprytna edycja tekstu w np. Notepad ++ zapewni ci wszystkie droga tam ... i stąd ten problem prawdopodobnie (i całkiem rozsądnie) zniechęci cię do robienia tego w ten sposób! Ale jest to dobre pytanie ogólne i dlatego zasługuje na odpowiedź dla każdego zainteresowanego przyszłością;-)