Построить запрос SQL динамически на основе типа данных столбцов - PullRequest
0 голосов
/ 16 февраля 2020

В моем приложении есть функция, с помощью которой пользователь может вводить несколько полей поиска, и они будут использоваться для запроса БД, например, пользователь вводит:

Смит 123456 Лондон 12 / 01/2020

Эти поля будут переданы хранимой процедуре в виде табличного параметра (состоящего из одного столбца как varchar). SP использует представление в качестве источника данных. Например, для указанного выше пользовательского поиска будет представление со следующими столбцами:

number, int
firstname, varchar
lastname, varchar
dob, datetime
address, varchar

SP должен динамически создавать запрос sql, и этот запрос должен выглядеть как

select * from customersview
where 'smith' in (firstname, lastname, address)
and 123456 in (number)
and 'london' in (firstname, lastname, address)
and '12/01/2029' in (dob)

Итак, что делает sp:

  1. Возьмите поисковые фильтры и определите, какой они тип данных
  2. Сопоставьте тип данных фильтров с типом данных столбцов, так что например, фильтр int сопоставлен со всеми int столбцами и т. д. c.

. Итак, я начал со следующего:

select COLUMN_NAME, DATA_TYPE
from INFORMATION_SCHEMA.VIEWS v
join INFORMATION_SCHEMA.COLUMNS c on c.TABLE_SCHEMA = v.TABLE_SCHEMA
and c.TABLE_NAME = v.TABLE_NAME
where c.TABLE_NAME = 'customersview'

, который будет дайте мне столбцы представления и их тип данных.

Но как я могу сопоставить типы данных (потому что фильтры входят в TVP), чтобы я мог строить различные условия?

В качестве альтернативы, я можно изменить TableType так, чтобы он имел 3 уникальных столбца (int, varchar, datetime), и приложение определяет тип данных и добавляет значение в правильный столбец.

1 Ответ

1 голос
/ 16 февраля 2020

Я только что попытался построить запрос, используя некоторое время l oop и проверив тип данных следующим образом.

Я добавил комментарии в самом запросе для простоты понимания.

TODO :

1- Вам необходимо добавить другие типы данных в следующем запросе.

2 - Вам необходимо параметризовать запрос и использовать sp_executesql вместо execute, чтобы избежать любой sql атаки с использованием инъекции .

--Table to Store search inputs, which will be your table type parameter.
DECLARE @v TABLE (searchString VARCHAR(100))
--Sample Inputs
INSERT INTO @v
SELECT *
FROM (
    VALUES ('smith')
        ,('1234')
        ,('london')
        ,('12/01/2020')
    ) t(v)

IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
    DROP TABLE #Temp
--Create a temporary table to loop the serach inputs
SELECT *
    ,0 AS IsProcessed
INTO #Temp
FROM @v

DECLARE @query NVARCHAR(max) = 'SELECT * FROM customersview WHERE 1 = 1 '
DECLARE @searchString VARCHAR(100)
--Loop through each search input
WHILE (
        SELECT Count(*)
        FROM #Temp
        ) > 0
BEGIN
    SELECT TOP 1 @searchString = searchString
    FROM #Temp

    SELECT @searchString


    --Check if input is int/bigint type
    IF (ISNUMERIC(@searchString) = 1)
    BEGIN


        SET @query = @query + 'AND ' + @searchString + ' IN (' + Stuff((
                    SELECT DISTINCT ', ' + Quotename(COLUMN_NAME)
                    FROM (
                        SELECT COLUMN_NAME
                            ,DATA_TYPE
                        FROM INFORMATION_SCHEMA.VIEWS v
                        JOIN INFORMATION_SCHEMA.COLUMNS c ON c.TABLE_SCHEMA = v.TABLE_SCHEMA
                            AND c.TABLE_NAME = v.TABLE_NAME
                        WHERE c.TABLE_NAME = 'customersview'
                            AND DATA_TYPE IN ('int', 'bigint')
                        ) t
                    FOR XML path('')
                        ,type
                    ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') + ')'
    END
    --Check if input is date type
    ELSE IF (ISDATE(@searchString) = 1)
    BEGIN

        SET @query = @query + ' AND ''' + @searchString + ''' IN (' + Stuff((
                    SELECT DISTINCT ', ' + Quotename(COLUMN_NAME)
                    FROM (
                        SELECT COLUMN_NAME
                            ,DATA_TYPE
                        FROM INFORMATION_SCHEMA.VIEWS v
                        JOIN INFORMATION_SCHEMA.COLUMNS c ON c.TABLE_SCHEMA = v.TABLE_SCHEMA
                            AND c.TABLE_NAME = v.TABLE_NAME
                        WHERE c.TABLE_NAME = 'customersview'
                            AND DATA_TYPE IN ('date', 'datetime')
                        ) t
                    FOR XML path('')
                        ,type
                    ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') + ')'
    END
    ELSE
    BEGIN
    --Check if input is VARCHAR/NVARCHAR type
        SET @query = @query + ' AND ''' + @searchString + ''' IN (' + Stuff((
                    SELECT DISTINCT ', ' + Quotename(COLUMN_NAME)
                    FROM (
                        SELECT COLUMN_NAME
                            ,DATA_TYPE
                        FROM INFORMATION_SCHEMA.VIEWS v
                        JOIN INFORMATION_SCHEMA.COLUMNS c ON c.TABLE_SCHEMA = v.TABLE_SCHEMA
                            AND c.TABLE_NAME = v.TABLE_NAME
                        WHERE c.TABLE_NAME = 'customersview'
                            AND DATA_TYPE IN ('VARCHAR', 'NVARCHAR')
                        ) t
                    FOR XML path('')
                        ,type
                    ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') + ')'
    END

    DELETE #Temp
    WHERE searchString = @searchString
END

SELECT @query
--Execute the query
--EXEC(@Query)
...