Ik heb een opgeslagen procedure voor je gemaakt.
Deze procedure onderzoekt de MSSQL-meta om een dynamische SQL-tekenreeks te bouwen die een resultaat retourneert met kolomnamen N
en hun waarden V
, en de bijbehorende rijsleutel K
waaruit die waarde is opgehaald, voor een opgegeven tabel.
Wanneer dit wordt uitgevoerd, worden de resultaten opgeslagen in een globale tijdelijke tabel met de naam ##ColumnsByValue, die vervolgens direct kan worden opgevraagd.
Maak de GetColumnsByValue
opgeslagen procedure, door dit script uit te voeren:
-- =============================================
-- 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
De SP neemt verschillende ingangen als parameters, deze worden uitgelegd in de volgende code.
Merk ook op dat ik een mechanisme heb geleverd om een "negerenlijst" als invoer toe te voegen:
- Hiermee kunt u alle kolomnamen weergeven die niet in de resultaten mogen worden opgenomen.
- U hoeft NIET de kolom toe te voegen die u als uw sleutel gebruikt, dwz de
row_id
van uw voorbeeldstructuur. - U MOET andere kolommen opnemen die niet
varchar
zijn aangezien deze een fout veroorzaken (omdat de SP gewoon eenvarchar
doet) vergelijking op alle kolommen waar het naar kijkt). - Dit wordt gedaan via een tijdelijke tabel die u moet maken/invullen
- Uw voorbeeldtabelstructuur suggereert dat de tabel alleen interessante kolommen bevat, dus dit is mogelijk niet op u van toepassing.
Ik heb voorbeeldcode toegevoegd om dit te doen (maar doe dit alleen als u nodig naar):
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');
Om nu de procedure te starten die uw tijdelijke resultatentabel maakt, gebruikt u de volgende code (en past u deze natuurlijk aan waar nodig).
-- 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
Dit geeft je ##ColumnsByValue
, waarop u elke zoekopdracht kunt uitvoeren die u nodig heeft, bijvoorbeeld:
select * from ##ColumnsByValue WHERE v = 'luxury' and k = 5 --some_row_id
U moet de opgeslagen procedure opnieuw uitvoeren (en, indien relevant, de negeerlijsttabel ervoor maken/wijzigen) voor elke tabel die u wilt onderzoeken.
Een punt van zorg bij deze aanpak is dat de nvarchar-lengte in uw geval kan worden overschreden. Je zou waarschijnlijk. moet een ander gegevenstype gebruiken, de lengte van de kolomnaam verkleinen, enz. Of verdeel het in substappen en voeg de resultaten samen om de gewenste resultatenset te krijgen.
Een andere zorg die ik heb, is dat dit complete overkill is voor jouw specifieke scenario, waar een eenmalig script-naar-query-venster je de basis zal geven van wat je nodig hebt, en dan zal een slimme tekstbewerking in bijvoorbeeld Notepad ++ je alle en daarom zal dit probleem je er waarschijnlijk (en redelijkerwijs) van weerhouden om het op deze manier te doen! Maar het is een goede algemene vraag, en verdient dus een antwoord voor iedereen die geïnteresseerd is in de toekomst;-)