osmo
osmo

Reputation: 87

SQL: Return Column names where column contains a given Value

i was wondering if there was a command/stored proc i can run in SQL Server that will give me the names of columns that contain given data within a table.

So if i was to query, give me all the columns in this table, that contain the value 75. i wouldnt want the row. just the column name within the table... is this possible?

Upvotes: 0

Views: 5045

Answers (2)

Aaron Bertrand
Aaron Bertrand

Reputation: 280252

-- input parameters (guessing on type for @value):

DECLARE 
  @schema SYSNAME = N'dbo', 
  @table  SYSNAME = N'z', 
  @value  VARCHAR(64) = '75';


-- now, inside the procedure body:

DECLARE @sql NVARCHAR(MAX) = N'SELECT ''cols:'' + STUFF(''''';

SELECT @sql += N' 
  + CASE WHEN EXISTS (SELECT 1 FROM ' 
  + QUOTENAME(@schema) + '.' + QUOTENAME(@table)
  + ' WHERE TRY_CONVERT(VARCHAR(64), ' + QUOTENAME(c.name) 
  + ') = @value) THEN '', ' + c.name + ''' ELSE '''' END'
FROM sys.tables AS t
INNER JOIN sys.columns AS c
ON t.[object_id] = c.[object_id]
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE t.name = @table AND s.name = @schema;

SET @sql += N', 1, 1, '''');'

PRINT @sql;

--EXEC sp_executesql @sql, N'@value VARCHAR(64)', @value;

When you are happy with the output, uncomment the EXEC.

So let's consider a simple table:

CREATE TABLE dbo.floob
(
  a INT, 
  b VARCHAR(32), 
  c VARBINARY(22), 
  d DATE, 
  e DATETIME, 
  f ROWVERSION
);

INSERT dbo.floob(a,b,c,d,e) VALUES
( 75, 'foo', 0x00, GETDATE(), GETDATE()),
( 21, '75',  0x00, GETDATE(), GETDATE());

Now, a stored procedure based on the above code:

CREATE PROCEDURE dbo.FindStringInAnyColumn
  @schema SYSNAME = N'dbo', 
  @table  SYSNAME,
  @value  VARCHAR(64)
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @sql NVARCHAR(MAX) = N'SELECT ''cols:'' + STUFF(''''';

  SELECT @sql += N' 
    + CASE WHEN EXISTS (SELECT 1 FROM ' 
    + QUOTENAME(@schema) + '.' + QUOTENAME(@table)
    + ' WHERE TRY_CONVERT(VARCHAR(64), ' + QUOTENAME(c.name) 
    + ') = @value) THEN '', ' + c.name + ''' ELSE '''' END'
  FROM sys.tables AS t
  INNER JOIN sys.columns AS c
  ON t.[object_id] = c.[object_id]
  INNER JOIN sys.schemas AS s
  ON t.[schema_id] = s.[schema_id]
  WHERE t.name = @table AND s.name = @schema;

  SET @sql += N', 1, 1, '''');'

  EXEC sp_executesql @sql, N'@value VARCHAR(64)', @value;
END
GO

Sample usage:

EXEC dbo.FindStringInAnyColumn @table = N'floob', @value = '75';

Output:

Cols: a, b

Upvotes: 3

Sebastian Becerra
Sebastian Becerra

Reputation: 1

I agree with the above comments that it sounds like you have a suboptimal schema design. You will probably run into performance issues if you try to do this on a large data set.

That said, you could unpivot the columns to convert them to rows. Here is an example lifted verbatim from the Using PIVOT and UNPIVOT article on Technet. Instead of 75, I used 4:

--Create the table and insert values as portrayed in the previous example.
CREATE TABLE pvt (VendorID int, Emp1 int, Emp2 int,
    Emp3 int, Emp4 int, Emp5 int);
GO
INSERT INTO pvt VALUES (1,4,3,5,4,4);
INSERT INTO pvt VALUES (2,4,1,5,5,5);
INSERT INTO pvt VALUES (3,4,3,5,4,4);
INSERT INTO pvt VALUES (4,4,2,5,5,4);
INSERT INTO pvt VALUES (5,5,1,5,5,5);
GO
--Unpivot the table.
SELECT VendorID, Employee, Orders
FROM 
   (SELECT VendorID, Emp1, Emp2, Emp3, Emp4, Emp5
   FROM pvt) p
UNPIVOT
   (Orders FOR Employee IN 
      (Emp1, Emp2, Emp3, Emp4, Emp5)
)AS unpvt
WHERE VendorID = 1 AND orders = 4;

This yields the following result:

+--+----------------------------------------+
|  | VendorID        Employee        Orders |
+--+----------------------------------------+
|  | 1               Emp1            4      |
|  | 1               Emp4            4      |
|  | 1               Emp5            4      |
+--+----------------------------------------+

Upvotes: 0

Related Questions