Manchmal steht man vor der Aufgabe entweder eine bestimmte Spalte bzw. einen bestimmten Spalteninhalt zu finden. So wurde ich kürzlich gefragt, warum in einer Applikation ein Datensatz nicht gelöscht werden kann. Als Fehlermeldung erhielt der Kunde, dass der Datensatz noch in einer anderen Tabelle referenziert werden würde. Anhand der Applikation konnte man nicht ermitteln in wo der Datensatz referenziert wird. Somit muss man sich auf der Datenbankebene auf die Suche nach der Tabelle mit den zu löschenden Datensatz machen und dann herausfinden, wo der Datensatz referenziert wird.

Suche nach einer bestimmten Spalte

In der ersten Ausbaustufe könnte man sich auf die Suche nach einer Spaltenbezeichnung machen, die den entsprechenden Inhalt vermuten lässt. So könnte man “Sehr geehrter Herr” in der Spalte “Anrede” vermuten.

-- =============================================
-- Author:	Erhard Rainer
-- Create date: 2014-12-13
-- Description:	Search for a certain columnname
-- =============================================
-- exec [dbo].[usp_SearchColumn] @columnname = 'class' , @exact = 1
-- exec [dbo].[usp_SearchColumn] @columnname = 'class' , @exact = 1, @type = 'int'
ALTER PROCEDURE [dbo].[usp_SearchColumn]
	 @columnName as nvarchar(MAX) = null,
	 @exact as bit = 1,
	 @type as nvarchar(100) = null
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

	Select 
		OBJECT_SCHEMA_NAME(o.object_id) as [Schema], 
		OBJECT_NAME(o.object_id) as [ObjectName],
		c.name as [Column],  
		st.name as [columnType]
		from sys.objects as o
		inner join sys.columns as c
		on o.object_id = c.object_id
		inner join sys.systypes as st
		on st.xtype = c.system_type_id
		Where 
			((@exact= 0 and c.name like '%' + @columnName +'%') or 
			(@exact=1 and c.name = @columnName))
		and 
			((@type is not null and st.name like '%' + @type + '%') or 
			(@type is null))
END
GO

Suche nach Inhalt einer bestimmten Spalte

Während die erste Methode nur nach einer Spalte namens “Anrede” sucht, könnte man das auch mit der Inhaltssuche kombinieren. dh. Suche “Sehr geehrter Herr” in allen Spalten, die Anrede lauten.

-- =============================================
-- Author:		Erhard Rainer
-- Create date: 2021-10-15
-- Description:	Search for a certain text in a certain column
-- =============================================
/*
EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = 'Alfred', 
	@ExactMatch = 0
EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = 'Alfreds Futterkiste', 
	@ExactMatch = 1
EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = 'Alfred', 
	@ExactMatch = 0, @ColumnName = 'CompanyName'
EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = 'Alfreds Futterkiste', 
	@ExactMatch = 1, @ColumnName = 'CompanyName'
EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = '57', 
	@ExactMatch = 0, @ColumnName = 'Address', @exactColumnName = 0
EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = '57', 
	@ExactMatch = 0, @type = 'int'
EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = 'Alfreds Futterkiste', 
	@ExactMatch = 0, @ColumnName = null
EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = 'Alfreds Futterkiste', 
	@ExactMatch = 0, @ColumnName = null, @debug = 1
*/
alter PROCEDURE [dbo].[usp_SearchColumnContent]
    @DataToFind NVARCHAR(MAX) = null,
    @ExactMatch BIT = 0,
	@ColumnName as nvarchar(MAX) = null,
	@exactColumnName as bit = 1,
	@type as nvarchar(100) = null,
	@output as bit = 1,
	@debug as bit = 0
AS
SET NOCOUNT ON

DECLARE @Temp TABLE(RowId INT IDENTITY(1,1), RowFoundID int, SchemaName sysname, 
	TableName sysname, ColumnName SysName, DataType VARCHAR(100), 
	DataFound BIT, [RowCount] int)

INSERT  INTO @Temp(SchemaName,TableName, ColumnName, DataType)
Select 
	OBJECT_SCHEMA_NAME(o.object_id) as [Schema], 
	OBJECT_NAME(o.object_id) as [ObjectName],
	c.name as [Column],  
	st.name as [columnType]
	from sys.objects as o
	inner join sys.columns as c
	on o.object_id = c.object_id
	inner join sys.systypes as st
	on st.xtype = c.system_type_id
	Where 
		((@exactColumnName= 0 and c.name like '%' + @columnName +'%') or 
		(@exactColumnName=1 and c.name = @columnName) or 
		(@ColumnName is null))
	and 
		((@type is not null and st.name like '%' + @type + '%') or 
		(@type is null and st.name in 
		('ntext','text','nvarchar','nchar','varchar','char')))
	and 
			OBJECT_SCHEMA_NAME(o.object_id) <> 'sys'

if @debug = 1 
Begin
	Select * from @Temp
End

DECLARE @i INT
DECLARE @MAX INT
DECLARE @TableName sysname
DECLARE @ColName sysname
DECLARE @SchemaName sysname
DECLARE @SQL NVARCHAR(4000)
DECLARE @RC as int
Set @i = 1
Set @MAX = (Select MAX(RowId) FROM  @Temp)

WHILE @i <= @MAX
    BEGIN
		Set @SchemaName =(Select SchemaName from @Temp where RowId = @i)
		Set @TableName =(Select TableName from @Temp where RowId = @i)
		Set @ColName =(Select ColumnName from @Temp where RowId = @i)
		if (@ExactMatch = 1)
		Begin 
			Set @SQL = 'Select @RC = Count(1) from ' + QUOTENAME(@SchemaName) + '.' 
			+ QUOTENAME(@TableName) + ' where Convert(nVarChar(4000), ' 
			+ QUOTENAME(@ColName) + ') = ''' + @DataToFind + '''' 
		end 
		else 
		begin 
			Set @SQL = 'Select @RC = Count(1) from ' + QUOTENAME(@SchemaName) + '.' 
			+ QUOTENAME(@TableName) + ' where Convert(nVarChar(4000), ' + 
			QUOTENAME(@ColName) + ') Like ''%' + @DataToFind + '%'''
		End

        if @debug = 1 PRINT @SQL
		EXECUTE sp_executeSQL @SQL, N'@RC INT OUTPUT', @RC OUTPUT

        IF @RC > 0 UPDATE @Temp SET DataFound = 1, [RowCount] = @RC WHERE RowId = @i

        SET @i = @i + 1
    END


if (@output = 1)
begin
	;with CTE as (
	Select ROW_NUMBER() over (order by [RowID] ASC) as RN,* from @Temp
	Where DataFound = 1
	)
	Update t
	Set T.RowFoundID = s.RN
	from @Temp as t 
	inner join CTE as s
	on t.RowId = s.RowId
	Select * from @Temp where DataFound = 1 order by  RowFoundID

	Set @i = 1
	Set @MAX = (Select MAX(RowFoundID) FROM  @Temp where DataFound = 1)

	if (@debug = 1)
	Begin
		Select * FROM  @Temp where DataFound = 1
	end

	WHILE @i <= @MAX
	begin
		Set @SchemaName =(Select SchemaName from @Temp where RowFoundID = @i)
		Set @TableName =(Select TableName from @Temp where RowFoundID = @i)
		Set @ColName =(Select ColumnName from @Temp where RowFoundID = @i)
		if (@ExactMatch = 1)
		Begin 
			Set @SQL = 'Select * from ' + QUOTENAME(@SchemaName) + '.' 
			+ QUOTENAME(@TableName) + ' where ' 
			+ QUOTENAME(@ColName) + ' = ''' + @DataToFind + '''' 
		end 
		else 
		begin 
			Set @SQL = 'Select * from ' + QUOTENAME(@SchemaName) + '.' 
			+ QUOTENAME(@TableName) + ' where ' 
			+ QUOTENAME(@ColName) + ' Like ''%' + @DataToFind + '%'''
		End
		if @debug = 1 PRINT @SQL
		exec(@SQL)
		Set @i = @i + 1
	end 
end 
else
Begin 
	SELECT  RowId,SchemaName,TableName, ColumnName, DataFound, [RowCount]
	FROM    @Temp
	WHERE   DataFound = 1
end
GO

Anbei ein paar Beispiele

  • Finde in jeder beliebigen Spalte den genauen Begriff “Alfreds Futterkiste”
    EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = ‘Alfreds Futterkiste’, @ExactMatch = 1
  • Finde in jeder beliebigen Spalte den ungefähren Begriff “Alfred”
    EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = ‘Alfred’, @ExactMatch = 0
  • Finde in der genauen Spalte “CompanyName” den ungefähren Begriff “Alfred”
    EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = ‘Alfred’, @ExactMatch = 0, @ColumnName = ‘CompanyName’
  • Finde in der genauen Spalte “CompayName” den genauen Begriff “Alfreds Futterkiste”
    EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = ‘Alfreds Futterkiste’, @ExactMatch = 1, @ColumnName = ‘CompanyName’
  • Suche in allen Integer Spalten nach der Zahl 57
    EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = ’57’, @ExactMatch = 0, @type = ‘int’
  • Suche in allen “Address” Spalten (auch ShipAddress usw.) nach der Hausnummer 57
    EXECUTE [dbo].[usp_SearchColumnContent] @DataToFind = ’57’, @ExactMatch = 0, @ColumnName = ‘Address’, @exactColumnName = 0
  • uva.

Mit dem Zusatzparameter @debug = 1 kann man sich die dahinterliegenden Abfragen ausgeben lassen.

Proprietäre Software

Neben reinen SQL Lösungen, kann man auch proprietäre Software installieren, die genau die selbe Funktion umfassen. Ich persönlich finden diesbezüglich Redgate SQL Search sehr gut und eine brauchbare Alternative zu oben angeführten Script.