Попытка переписать хранимую процедуру, чтобы можно было передать @tableName из C#

#c# #sql-server #stored-procedures

#c# #sql-server #хранимые процедуры

Вопрос:

Я пытаюсь передать имя таблицы хранимой процедуре в SQL Server.

Я понял, что по умолчанию это невозможно сделать из-за рисков для injection malicious attacks .

Когда я поискал в Google, я понял, что это в любом случае возможно сделать.

Я все равно попытаюсь создать это, поскольку я понял, что это возможно каким-то образом, объявив string query , как я пытаюсь сделать ниже. Зная о рисках, я все равно попытаюсь это сделать, поскольку мне действительно нравится создавать весь код в процедурах, чтобы сделать его более чистым. Код также будет использоваться только на моем собственном компьютере.

Исходный код, который у меня есть для хранимой процедуры, выглядит следующим образом. Я поместил @tableName здесь в качестве параметра, который затем не работает, поскольку по умолчанию невозможно передать здесь имя таблицы.

 CREATE PROCEDURE getLastFeatureUpdate
    @maxDateTime BIGINT = 0,
    @tableName nvarchar
AS
    SELECT
        test.FeatureNbr,
        test.DayTime,
        test.Val
    FROM
        @tableName test 
    WHERE
        DayTime = (SELECT MAX(DayTime)
                   FROM @tableName
                   WHERE FeatureNbr = test.FeatureNbr 
                     AND DayTime <= @maxDateTime)
  

Теперь появляется версия, которую я пытаюсь сделать, чтобы переписать приведенный выше код, чтобы он работал, передавая @tableName параметр — я получаю 2 ошибки для этого кода:

System.Data.SqlClient.SQLException: ‘Необходимо объявить скалярную переменную «@maxDateTime»

EXEC sp_executesql @FullQuery показывает ошибку:

Ошибка: процедура: [dbo].[getLastFeatureUpdate] имеет неразрешенную ссылку на объект [dbo].[sp_executesql]

Затем я задаюсь вопросом, что я могу делать неправильно в своем коде?

Переписанная хранимая процедура:

 CREATE PROCEDURE getLastFeatureUpdate
    @maxDateTime BIGINT = 0,
    @tableName nvarchar
AS
BEGIN
    DECLARE @FullQuery nvarchar(1000)

    SET @FullQuery = N'SELECT test.FeatureNbr, test.DayTime, test.Val FROM '   QUOTENAME(@tableName)   ' test 
                     WHERE DayTime = ( SELECT MAX(DayTime) FROM '   QUOTENAME(@tableName)   ' WHERE FeatureNbr = test.FeatureNbr AND DayTime <= @maxDateTime )'

    EXEC sp_executesql @FullQuery
END
  

Редактировать:

C # код для выполнения хранимой процедуры:

         void getLastFeatureUpdate()
        {
            using (SqlConnection conn = new SqlConnection(GetConnectionString()))
            {
                conn.Open();

                // 1.  create a command object identifying the stored procedure
                SqlCommand cmd = new SqlCommand("getLastFeatureUpdate", conn);

                // 2. set the command object so it knows to execute a stored procedure
                cmd.CommandType = CommandType.StoredProcedure;

                // 3. add parameter to command, which will be passed to the stored procedure
                cmd.Parameters.Add(new SqlParameter("@maxDateTime", 201010222045));
                cmd.Parameters.Add(new SqlParameter("@tableName", "testTable"));

                // execute the command
                using (SqlDataReader rdr = cmd.ExecuteReader())
                {
                    // iterate through results, printing each to console
                    while (rdr.Read())
                    {
                        Int64 v1 = (Int64)rdr["DayTime"];
                        int v2 = (Int16)rdr["FeatureNbr"];
                        double v3 = (double)rdr["Val"];

                        MessageBox.Show(v1   ","   v2   ","   v3);
                    }
                }
            }
        }
  

Комментарии:

1. Вы должны объявить имя вашей таблицы как тип «sysname», просто к вашему сведению. «sysname» — это тип всех объектов схемы … имена таблиц, имена столбцов, имена ограничений по умолчанию, имена внешних ключей, имена индексов и т.д.

Ответ №1:

Вы должны определить и передать параметры при использовании sp_executesql для запроса, который принимает параметры:

 EXECUTE sp_executesql 
    @FullQuery, 
    N'@maxDateTime float(53)',  
    @maxDateTime ; 
  

Я скажу, что использование динамического имени таблицы является признаком того, что у вас плохой дизайн таблицы, особенно когда у вас похожие данные, разделенные на несколько таблиц, а не на одну таблицу со столбцами для категоризации данных.

Комментарии:

1. Я буду жестко кодировать все, чтобы иметь уникальные имена. Это не может быть ошибкой, если я делаю это правильно. В программном обеспечении не будет никакого пользовательского ввода.

2. AddWithValue удобно, но, по крайней мере, с SQL Server, это плохой вариант. У этого есть нюансы, и вы находитесь в центре их влияния. Читайте: можем ли мы уже прекратить использовать AddWithValue() ? Вам нужно прочитать документацию по Add методам. Не просто догадывайтесь. Что касается исключения, вы внесли изменения, не поделившись ими, поэтому нет никакого способа помочь вам с этим.

3. Как вам было сказано в последнем вопросе, text и ntext они устарели. varchar(max) nvarchar(max) Вместо этого используйте и . Кроме того, изменение на BIGINT не является правильной реакцией. Измените его на DATETIME и передайте DateTime значение.

4. Мне сказали: varchar or nvarchar нет nvarchar(max) , но nvarchar(max) сработало. Тогда я должен использовать это, поэтому я не использую text , чтобы оно было совместимо с более новыми версиями. Как я уже упоминал. Я изменил на BIGINT, чтобы найти другую актуальную проблему. Я разберусь, для чего предназначен формат DateTime .

5. Обратите внимание, что у DateTime типа нет формата. Строковые представления имеют формат, и вы анализируете этот формат DateTime .