@RowFrom int
@RowTo int
are both Global Input Params for the Stored Procedure, and since I am compiling the SQL query inside the Stored Procedure with T-SQL then using Exec(@sqlstatement)
at the end of the stored procedure to show the result, it gives me this error when I try to use the @RowFrom
or @RowTo
inside the @sqlstatement
variable that is executed.. it works fine otherwise.. please help.
"Must declare the scalar variable "@RowFrom"."
Also, I tried including the following in the @sqlstatement
variable:
'Declare @Rt int'
'SET @Rt = ' + @RowTo
but @RowTo
still doesn’t pass its value to @Rt
and generates an error.
hofnarwillie
3,55310 gold badges48 silver badges73 bronze badges
asked Aug 24, 2011 at 20:39
1
You can’t concatenate an int to a string. Instead of:
SET @sql = N'DECLARE @Rt int; SET @Rt = ' + @RowTo;
You need:
SET @sql = N'DECLARE @Rt int; SET @Rt = ' + CONVERT(VARCHAR(12), @RowTo);
To help illustrate what’s happening here. Let’s say @RowTo = 5.
DECLARE @RowTo int;
SET @RowTo = 5;
DECLARE @sql nvarchar(max);
SET @sql = N'SELECT ' + CONVERT(varchar(12), @RowTo) + ' * 5';
EXEC sys.sp_executesql @sql;
In order to build that into a string (even if ultimately it will be a number), I need to convert it. But as you can see, the number is still treated as a number when it’s executed. The answer is 25, right?
In your case you can use proper parameterization rather than use concatenation which, if you get into that habit, you will expose yourself to SQL injection at some point (see this and this:
SET @sql = @sql + ' WHERE RowNum BETWEEN @RowFrom AND @RowTo;';
EXEC sys.sp_executesql @sql,
N'@RowFrom int, @RowTo int',
@RowFrom, @RowTo;
answered Aug 24, 2011 at 21:01
Aaron BertrandAaron Bertrand
272k36 gold badges465 silver badges487 bronze badges
4
You can also get this error message if a variable is declared before a GO
and referenced after it.
See this question and this workaround.
answered Mar 25, 2019 at 22:11
Pierre CPierre C
2,76035 silver badges32 bronze badges
Just FYI, I know this is an old post, but depending on the database COLLATION settings you can get this error on a statement like this,
SET @sql = @Sql + ' WHERE RowNum BETWEEN @RowFrom AND @RowTo;';
if for example you typo the S in the
SET @sql = @***S***ql
sorry to spin off the answers already posted here, but this is an actual instance of the error reported.
Note also that the error will not display the capital S in the message, I am not sure why, but I think it is because the
Set @sql =
is on the left of the equal sign.
answered Apr 1, 2015 at 19:13
htm11hhtm11h
1,7298 gold badges46 silver badges103 bronze badges
0
This is most likely not an answer to the issue itself, but this question pops up as first result when searching for Sql declare scalar variable
hence I want to share a possible solution to this error.
In my case this error was caused by the use of ;
after a SQL statement. Just remove it and the error will be gone.
I guess the cause is the same as @IronSean already posted in a comment above:
it’s worth noting that using GO (or in this case causes a new branch where declared variables aren’t visible past the statement.
For example:
DECLARE @id int
SET @id = 78
SELECT * FROM MyTable WHERE Id = @var; <-- remove this character to avoid the error message
SELECT * FROM AnotherTable WHERE MyTableId = @var
answered Nov 5, 2020 at 16:25
ViRuSTriNiTyViRuSTriNiTy
5,0072 gold badges31 silver badges56 bronze badges
5
Sometimes, if you have a ‘GO’ statement written after the usage of the variable, and if you try to use it after that, it throws such error. Try removing ‘GO’ statement if you have any.
answered May 24, 2021 at 6:12
Just adding what fixed it for me, where misspelling is the suspect as per this MSDN blog…
When splitting SQL strings over multiple lines, check that that you are comma separating your SQL string from your parameters (and not trying to concatenate them!) and not missing any spaces at the end of each split line. Not rocket science but hope I save someone a headache.
For example:
db.TableName.SqlQuery(
"SELECT Id, Timestamp, User " +
"FROM dbo.TableName " +
"WHERE Timestamp >= @from " +
"AND Timestamp <= @till;" + [USE COMMA NOT CONCATENATE!]
new SqlParameter("from", from),
new SqlParameter("till", till)),
.ToListAsync()
.Result;
EBH
10.3k3 gold badges32 silver badges59 bronze badges
answered Jun 21, 2017 at 15:46
Tim TylerTim Tyler
2,3112 gold badges15 silver badges13 bronze badges
1
Case Sensitivity will cause this problem, too.
@MyVariable and @myvariable are the same variables in SQL Server Man. Studio and will work. However, these variables will result in a «Must declare the scalar variable «@MyVariable» in Visual Studio (C#) due to case-sensitivity differences.
answered Jun 9, 2016 at 11:20
Just an answer for future me (maybe it helps someone else too!). If you try to run something like this in the query editor:
USE [Dbo]
GO
DECLARE @RC int
EXECUTE @RC = [dbo].[SomeStoredProcedure]
2018
,0
,'arg3'
GO
SELECT month, SUM(weight) AS weight, SUM(amount) AS amount
FROM SomeTable AS e
WHERE year = @year AND type = 'M'
And you get the error:
Must declare the scalar variable «@year»
That’s because you are trying to run a bunch of code that includes BOTH the stored procedure execution AND the query below it (!). Just highlight the one you want to run or delete/comment out the one you are not interested in.
marc_s
729k175 gold badges1327 silver badges1455 bronze badges
answered Jul 21, 2019 at 18:05
saiyancodersaiyancoder
1,2752 gold badges13 silver badges20 bronze badges
If someone else comes across this question while no solution here made my sql file working, here’s what my mistake was:
I have been exporting the contents of my database via the ‘Generate Script’ command of Microsofts’ Server Management Studio and then doing some operations afterwards while inserting the generated data in another instance.
Due to the generated export, there have been a bunch of «GO» statements in the sql file.
What I didn’t know was that variables declared at the top of a file aren’t accessible as far as a GO statement is executed. Therefore I had to remove the GO statements in my sql file and the error «Must declare the scalar variable xy» was gone!
answered Oct 19, 2020 at 10:33
pburpbur
757 bronze badges
As stated in https://learn.microsoft.com/en-us/sql/t-sql/language-elements/sql-server-utilities-statements-go?view=sql-server-ver16 , the scope of a user-defined variable is batch dependent .
—This will produce the error
GO
DECLARE @MyVariable int;
SET @MyVariable = 1;
GO --new batch of code
SELECT @MyVariable--CAST(@MyVariable AS
int);
GO
—This will not produce the error
GO
DECLARE @MyVariable int;
SET @MyVariable = 1;
SELECT @MyVariable--CAST(@MyVariable AS int);
GO
We get the same error when we try to pass a variable inside a dynamic SQL:
GO
DECLARE @ColumnName VARCHAR(100),
@SQL NVARCHAR(MAX);
SET @ColumnName = 'FirstName';
EXECUTE ('SELECT [Title],@ColumnName FROM Person.Person');
GO
—In the case above @ColumnName is nowhere to be found, therefore we can either do:
EXECUTE ('SELECT [Title],' +@ColumnName+ ' FROM Person.Person');
or
GO
DECLARE @ColumnName VARCHAR(100),
@SQL NVARCHAR(MAX);
SET @ColumnName = 'FirstName';
SET @SQL = 'SELECT ' + @ColumnName + ' FROM Person.Person';
EXEC sys.sp_executesql @SQL
GO
answered Sep 15, 2022 at 10:39
Give a ‘GO’ after the end statement and select all the statements then execute
answered Dec 29, 2021 at 15:23
1
Цитата из документации Database Identifiers:
Rules for Regular Identifiers
- Embedded spaces or special characters are not allowed.
В именах параметров не разрешены пробелы.
Уберём из них пробелы и квадратные скобки.
Попутно исправим другие ошибки и недочёты: имя соединения (у вас оно почему-то названо connectionString
), опасность потери ресурсов (используем using
для устранения этого), использование устаревшего и опасного метода AddWithValue
(заменим его на Add
с указанием точного типа).
Кроме того, время_входаTextBox
используется дважды. Будьте внимательны!
string sql = @"UPDATE Табель SET Статус = @Статус, [Код смены] = @КодСмены, [Время входа] = @ВремяВхода, [Время выхода] = @ВремяВыхода WHERE [Код сотрудника] = @КодСотрудника AND Дата = @Дата AND [Код табеля] = @КодТабеля";
using var connection = new SqlConnection(_connectionString);
connection.Open();
using var command = new SqlCommand(sql, connection);
command.Parameters.Add("КодТабеля", SqlDbType.Int).Value = textBox5.Text; // int.Parse(textBox5.Text)
command.Parameters.Add("КодСотрудника", SqlDbType.Int).Value = код_сотрудникаTextBox.Text;
command.Parameters.Add("Дата", SqlDbType.DateTime2).Value = датаDateTimePicker.Value;
command.Parameters.Add("Статус", SqlDbType.NVarChar).Value = comboBox1.Text;
command.Parameters.Add("КодСмены", SqlDbType.Int).Value = comboBox2.Text;
command.Parameters.Add("ВремяВхода", SqlDbType.DateTime2).Value = время_входаTextBox.Text;
command.Parameters.Add("ВремяВыхода", SqlDbType.DateTime2).Value = время_выходаTextBox.Text;
command.ExecuteNonQuery();
Благодаря использованию using
соединение будет гарантировано закрыто, даже в случае возникновения исключения. Вызывать метод Close()
не нужно.
Я не знаю, какие именно типы используются у вас в таблице, поэтому сами укажите правильные типы SqlDbType
. При необходимости примените int.Parse
и тому подобные методы. А лучше используйте NumericUpDown
для ввода чисел вместо TextBox
.
Почему не стоит использовать метод AddWithValue
:
Can we stop using AddWithValue() already?
AddWithValue is evil!
AddWithValue is Evil
Достаточно прочитать любую из этих статей.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
--МАССОВАЯ ВСТАВКА НЕСКОЛЬКИХ ФАЙЛОВ_CSV ИЗ ПАПКИ --Пока что ругается на курсор IF (OBJECT_ID('tempdb..#csv_temp') IS NOT NULL) DROP TABLE #csv_temp; CREATE TABLE #csv_temp( [Times] VARCHAR (100), [Caller_Name] INT, [Caller_Number] INT, [Callee_Name] INT, [Callee_Numbers] VARCHAR(100), [DOD] VARCHAR(100), [DID] VARCHAR(100), [Call_Duration_(s)] INT, [Talk_Duration_(s)] INT, [STATUS] VARCHAR(100), [Source_Trunk] VARCHAR(50), [Destination_Trunk] VARCHAR(100), [Communication_Type] VARCHAR(100), [PIN_Code] VARCHAR(10), [Caller_IP_Address] VARCHAR(200), [Cost] VARCHAR(100), [Billing_Account] VARCHAR(100) ) -- Переменые DECLARE @filename VARCHAR(255), @path VARCHAR(255), @SQL VARCHAR(8000), @cmd VARCHAR(1000), @Times VARCHAR(1000), @Caller_Name INT, @Caller_Number INT, @Callee_Name INT, @Callee_Numbers VARCHAR(100), @DOD VARCHAR(100), @DID VARCHAR(100), @Call_Duration_(s)INT, @Talk_Duration_(s) INT, @STATUS VARCHAR(100), @Source_Trunk VARCHAR(50), @Destination_Trunk VARCHAR(100), @Communication_Type VARCHAR(100), @PIN_Code VARCHAR(10), @Caller_IP_Address VARCHAR(200), @Cost VARCHAR(100), @Billing_Account VARCHAR(100) --получить список файлов для обработки: SET @path = 'C:serg' SET @cmd = 'dir ' + @path + '*.csv /b' INSERT INTO #csv_temp ( [Times] , [Caller_Name], [Caller_Number], [Callee_Name], [Callee_Numbers] , [DOD], [DID], [Call_Duration_(s)], [Talk_Duration_(s)], [Status], [Source_Trunk], [Destination_Trunk], [Communication_Type], [PIN_Code], [Caller_IP_Address], [Cost], [Billing_Account] ) --SELECT '17' VALUES ( (SELECT [Times] FROM #csv_temp ), (SELECT [Caller_Name] FROM #csv_temp ), (SELECT [Caller_Number] FROM #csv_temp ), (SELECT [Callee_Name] FROM #csv_temp ), (SELECT [Callee_Numbers] FROM #csv_temp ), (SELECT [DOD] FROM #csv_temp ), (SELECT [DID] FROM #csv_temp ), (SELECT [Call_Duration_(s)] FROM #csv_temp ), (SELECT [Talk_Duration_(s)] FROM #csv_temp ), (SELECT [Status] FROM #csv_temp ), (SELECT [Source_Trunk] FROM #csv_temp ), (SELECT [Destination_Trunk] FROM #csv_temp ), (SELECT [Communication_Type] FROM #csv_temp ), (SELECT [PIN_Code] FROM #csv_temp ), (SELECT [Caller_IP_Address] FROM #csv_temp ), (SELECT [Cost] FROM #csv_temp ), (SELECT [Billing_Account] FROM #csv_temp ) ) EXEC Master..xp_cmdShell @cmd UPDATE #csv_temp SET [Times] = @path where [Times] is null -- Курсор declare c1 cursor for SELECT [Times], [Caller_Name], [Caller_Number], [Callee_Name], [Callee_Numbers] , [DOD], [DID], [Call_Duration_(s)], [Talk_Duration_(s)], [Status], [Source_Trunk], [Destination_Trunk], [Communication_Type], [PIN_Code], [Caller_IP_Address], [Cost], [Billing_Account] FROM #csv_temp where Times like '%.csv%' --Открываем Курсор open c1 --- выборка данных ---fetch next from c1 into @path,@filename fetch next from c1 into @Times,@Caller_Name,@Caller_Number, @Callee_Name,@Callee_Numbers,@DOD,@DID,@Call_Duration_(s), @Talk_Duration_(s),@Status,@Source_Trunk,@Destination_Trunk, @Communication_Type,@PIN_Code,@Caller_IP_Address,@Cost,@Billing_Account While @@fetch_status <> -1 begin --bulk insert won't take a variable name, so make a SQL AND EXECUTE it instead: SET @SQL = 'BULK INSERT #csv_temp FROM ''' + @path + @filename + ''' ' + ' WITH ( FIELDTERMINATOR = '';'', ROWTERMINATOR = ''0x0a'', FIRSTROW = 2 ) ' -- Вывод результата print @SQL EXEC (@SQL) -- fetch NEXT FROM c1 INTO @path,@filename END close c1 deallocate c1 -------------------------------------------------------------------------------------- |
Задача: разделить данные за сегодня и вчера по столбцам. Если использовать этот запрос без переменных то все работает. Анализ синтаксиса в excel пишет «необходимо объявить скалярную переменную @today» хотя я вроде его объявил в начале Используется MSSQL 2016
DECLARE @today as Date, @yesterday as Date;
Set @today = convert(date, getdate());
Set @yesterday = convert(date, dateadd(day, -1, getdate()));
SELECT n.Name, o.Created,
Count(DISTINCT(CASE WHEN Status = 'N' And o.Date = @today Then ID END)) as NewQ,
Count(DISTINCT(CASE WHEN Status = 'N' And o.Date = @yesterday Then ID END)) as YdNewQ,
Count(DISTINCT(CASE WHEN Status = 'W' And o.Date = @today Then ID END)) as WaitingQ,
Count(DISTINCT(CASE WHEN Status = 'W' And o.Date = @yesterday Then ID END)) as YDWaitingQ,
Count(DISTINCT(CASE WHEN Status = 'U' And o.Date = @today Then ID END)) as ProblemQ,
Count(DISTINCT(CASE WHEN Status = 'U' And o.Date = @yesterday Then ID END)) as YdProblemQ,
Count(DISTINCT(CASE WHEN Status = 'Z' And o.Date = @today Then ID END)) as CancelledQ,
Count(DISTINCT(CASE WHEN Status = 'Z' And o.Date = @yesterday Then ID END)) as YdCancelledQ
FROM Orders i
LEFT JOIN OrderItems o ON o.OrderID = i.ID
LEFT JOIN NomenclUS m ON m.ID = o.ProductID
WHERE i.Status <> 'Z' AND o.Created >= dateadd(day, -2, getdate())
GROUP BY o.Created, n.CatID, n.CatName
ORDER BY o.Created, n.CatID
Причина, по которой вы получаете DECLARE
Ошибка в вашем динамическом операторе заключается в том, что динамические операторы обрабатываются отдельными пакетами, что сводится к области видимости. Хотя может быть более формальное определение областей, доступных в SQL Server, я считаю, что достаточно иметь в виду следующие три, упорядоченные от самой высокой доступности до самой низкой доступности:
Глобальный:
Объекты, доступные для всего сервера, такие как временные таблицы, созданные с двойным знаком хеш / фунт (##GLOBALTABLE
Однако вы хотели бы позвонить #). Будьте очень осторожны с глобальными объектами, так же, как с любым приложением, SQL Server или другим способом; Эти типы вещей, как правило, лучше всего избегать вообще. По сути, я говорю о том, чтобы помнить об этом как об отдельном напоминании, чтобы не вмешиваться.
IF ( OBJECT_ID( 'tempdb.dbo.##GlobalTable' ) IS NULL )
BEGIN
CREATE TABLE ##GlobalTable
(
Val BIT
);
INSERT INTO ##GlobalTable ( Val )
VALUES ( 1 );
END;
GO
-- This table may now be accessed by any connection in any database,
-- assuming the caller has sufficient privileges to do so, of course.
Сессия:
Объекты, ссылки на которые привязаны к определенному спиду. На мой взгляд, единственный тип объекта сеанса, о котором я могу думать, это обычная временная таблица, определенная как #Table. Нахождение в области действия сеанса по сути означает, что после пакета (завершается GO
), ссылки на этот объект будут продолжать разрешаться успешно. Они технически доступны для других сессий, но это было бы неким подвигом сделать так программно, так как они получают своего рода рандомизированные имена в базе данных tempdb, и доступ к ним в любом случае является проблемой в заднице.
-- Start of session;
-- Start of batch;
IF ( OBJECT_ID( 'tempdb.dbo.#t_Test' ) IS NULL )
BEGIN
CREATE TABLE #t_Test
(
Val BIT
);
INSERT INTO #t_Test ( Val )
VALUES ( 1 );
END;
GO
-- End of batch;
-- Start of batch;
SELECT *
FROM #t_Test;
GO
-- End of batch;
При открытии нового сеанса (соединение с отдельным spid) второй пакет выше не будет выполнен, так как этот сеанс не сможет решить #t_Test
имя объекта
Пакет:
Нормальные переменные, такие как ваш @value1
а также @value2
, предназначены только для партии, в которой они заявлены. В отличие от #Temp
таблицы, как только ваш блок запроса достигает GO
эти переменные перестают быть доступными для сеанса. Это уровень области, который генерирует вашу ошибку.
-- Start of session;
-- Start of batch;
DECLARE @test BIT = 1;
PRINT @test;
GO
-- End of batch;
-- Start of batch;
PRINT @Test; -- Msg 137, Level 15, State 2, Line 2
-- Must declare the scalar variable "@Test".
GO
-- End of batch;
Хорошо, и что?
Что происходит здесь с вашим динамическим утверждением, что EXECUTE()
Команда эффективно оценивает как отдельный пакет, не прерывая пакет, из которого вы его выполнили. EXECUTE()
это хорошо и все, но с момента введения sp_executesql()
Я использую первое только в самых простых случаях (явно, когда в моих высказываниях совсем мало «динамических» элементов, в первую очередь, чтобы «обмануть» иным образом неаккомодирующий DDL CREATE
заявления, чтобы бежать в середине других партий). Aaron Bertrand, приведенный выше, аналогичен и по производительности будет аналогичен приведенному ниже, используя функцию оптимизатора при оценке динамических операторов, но я подумал, что стоит остановиться на @param
Ну, параметр.
IF NOT EXISTS ( SELECT 1
FROM sys.objects
WHERE name = 'TblTest'
AND type = 'U' )
BEGIN
--DROP TABLE dbo.TblTest;
CREATE TABLE dbo.TblTest
(
ID INTEGER,
VALUE1 VARCHAR( 1 ),
VALUE2 VARCHAR( 1 )
);
INSERT INTO dbo.TblTest ( ID, VALUE1, VALUE2 )
VALUES ( 61, 'A', 'B' );
END;
SET NOCOUNT ON;
DECLARE @SQL NVARCHAR( MAX ),
@PRM NVARCHAR( MAX ),
@value1 VARCHAR( MAX ),
@value2 VARCHAR( 200 ),
@Table VARCHAR( 32 ),
@ID INTEGER;
SET @Table = 'TblTest';
SET @ID = 61;
SET @PRM = '
@_ID INTEGER,
@_value1 VARCHAR( MAX ) OUT,
@_value2 VARCHAR( 200 ) OUT';
SET @SQL = '
SELECT @_value1 = VALUE1,
@_value2 = VALUE2
FROM dbo.[' + REPLACE( @Table, '''', '' ) + ']
WHERE ID = @_ID;';
EXECUTE dbo.sp_executesql @statement = @SQL, @param = @PRM,
@_ID = @ID, @_value1 = @value1 OUT, @_value2 = @value2 OUT;
PRINT @value1 + ' ' + @value2;
SET NOCOUNT OFF;