sql-serversearchdynamicinformation-schematablename

Search table and column names having some known range of values using MS SQL server


Environment: SQL Server 2016 (v13.00.4259), where I have read-only permissions (basically just SELECT from multiple tables).

Use case: I want to find all table and column names from a database, where the column type is datetime and the column name has %END% in it, and any value (in the table.column) is found to be between some time frame (in this case one day).

I can find all table and column names from the database using this:

SELECT TABLE_NAME as tab, COLUMN_NAME as col
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE = 'datetime' and COLUMN_NAME like '%END%';

I guess I'd need a dynamic query, because table and column names should be static in static SQL query, so:

WITH enddates AS 
(
    SELECT TABLE_NAME AS tab, COLUMN_NAME AS col
    FROM INFORMATION_SCHEMA.COLUMNS
    WHERE DATA_TYPE = 'datetime' AND  COLUMN_NAME LIKE '%END%'
)
exec('select tab, col 
      from enddates
      where ' + tab + '.' + col + ' >= ''2019-10-21'' and ' 
        + tab + '.' + col + '< ''2019-10-22'' ')

  -- would want it to eval to:
  -- SELECT tab, col FROM enddates 
  -- WHERE $tab.$col >= '2019-10-21' AND $tab.$col < '2019-10-22'

Would want to get a list of table and column names where some %END% datetime is in 2019-10-21.

But it does not work:

"SQL Error [156] [S1000]: Incorrect syntax near the keyword 'exec'."

One reason could be, the 'enddates' alias for sub-query is not seen inside EXEC, but I assume that is not the only problem. For clarity I think it shows what I am after.

I have worked more with PostgreSQL, but now I need to retrieve this kind of data from SQL Server with readonly access, so cannot make toy data tables there to test, nor views. Would of course be simpler first to create a table of those interesting table.column pairs and avoid needing a sub-query. But yet I wouldn't know how to make dynamic queries from those table and column names then in SQL Server.

Edit: another more semantically correct "pseudo-code" idea, using previous WITH-sub-query:

SELECT tab, col 
FROM enddates 
WHERE EXEC('select ' + tab + '.' + col + ' >= ''2019-10-21'' and ' + tab + '.' + col + ' < ''2019-10-22''') = True

Solution

  • One possible approach is to generate and execute a dynamic statement:

    -- Dynamic statement
    DECLARE @stm nvarchar(max) = N''
    
    SELECT @stm = CONCAT(
        @stm,
        CASE WHEN @stm = N'' THEN N'' ELSE N' UNION ALL ' END,
        N'SELECT ''',
        QUOTENAME(TABLE_NAME),
        N''' AS Tab, ''',
        QUOTENAME(COLUMN_NAME),
        N''' AS Col, COUNT(*) AS Cnt FROM ',
        QUOTENAME(TABLE_NAME),
        N' WHERE ',
        QUOTENAME(COLUMN_NAME),
        N' >= ''20191021'' AND ',
        QUOTENAME(COLUMN_NAME),
        N' < ''20191022'' ',
        N'HAVING COUNT(*) > 0'
    )
    FROM INFORMATION_SCHEMA.COLUMNS
    WHERE DATA_TYPE = 'datetime' and COLUMN_NAME LIKE '%END%'
    
    -- Validate and execute
    PRINT @stm
    EXEC sp_executesql @stm