C#/SQL 服务器:从 C# 代码创建存储过程时出错
C# / SQL Server : error when creating stored procedure from C# code
以下代码在 C# Winforms 中执行。我要做的是在程序启动时创建表和存储过程。我在 SQL 命令中使用 IF NOT EXISTS
。如果不存在,则创建,否则什么也不做。
创建表格没问题,效果很好。
但是在创建存储过程时,Visual Studio 抛出这个错误;
Must declare the scalar variable @xxxx
在收到此错误之前,我使用了 USE
和 GO
命令,这种方法也出现了 CREATE/ALTER PROCEDURE
错误。当我在网上研究时,我逐渐了解到 USE
和 GO
在 C# 中是禁忌。这就是为什么我在 IF NOT EXISTS
.
之后使用 Else
而不是 GO
代码在 T-SQL 中有效,但我想我需要以某种方式将其转换为 C#。
感谢任何帮助。谢谢。
更新:
我把StringBuilder改成just string后,修复了之前的错误。现在它给出了下面的一个。虽然找不到任何问题。
" System.Data.SqlClient.SqlException
HResult=0x80131904
Message=Incorrect Syntax near the 'Trendyol'.
Unclosed quotation mark after the character string "', ')))))AS varchar) FROM urnBilgi INNER JOIN urn ON urn.stkID = bVeriID WHERE bVeriID = @stkID and bBilgiID = @bBilgiID AND(urn.fiyatS BETWEEN @fiyat_baslangic and @fiyat_bitis) END END'"
public void createSP()
{
StringBuilder sbSP = new StringBuilder();
sbSP.AppendLine(" IF NOT EXISTS(SELECT * FROM sys.objects WHERE type = 'P' AND object_id = object_id('etradeCore'))" +
" exec('CREATE PROCEDURE [dbo].[etradeCore](" +
" ELSE"+ //Used Instead of GO
" ALTER PROCEDURE [dbo].[etradeCore](" +
" @pazaryeri VARCHAR(50)," +
" @magaza VARCHAR(50)," +
" @stkID int," +
" @komisyon decimal(9, 4)," +
" @fiyat_baslangic decimal(9, 4)," +
" @fiyat_bitis decimal(9, 4)," +
" @eklenecekfiyat decimal(9, 4)" +
")" +
" AS BEGIN" +
" SET NOCOUNT OFF" +
" SET ANSI_NULLS ON" +
" SET QUOTED_IDENTIFIER ON" +
" DECLARE @bBilgiID tinyint" +
" IF(@pazaryeri = 'Trendyol' and @magaza = 'Kozmeti')" +
" SET @bBilgiID = 58" +
" ELSE IF(@pazaryeri = 'Trendyol' and @magaza = 'Golden Rose')" +
" SET @bBilgiID = 52" +
" ELSE IF(@pazaryeri = 'Trendyol' and @magaza = 'Ziaja')" +
" SET @bBilgiID = 60" +
" ELSE IF(@pazaryeri = 'Hepsiburada' and @magaza = 'Kozmeti')" +
" SET @bBilgiID = 43" +
" ELSE IF(@pazaryeri = 'Hepsiburada' and @magaza = 'Golden Rose')" +
" SET @bBilgiID = 44" +
" ELSE IF(@pazaryeri = 'Hepsiburada' and @magaza = 'Ziaja')" +
" SET @bBilgiID = 43" +
" IF NOT EXISTS(select * from urnBilgi WHERE bVeriID = @stkID and bBilgiID = @bBilgiID and bDeger >= convert(varchar, 0))" +
" BEGIN" +
" INSERT INTO urnBilgi(bVeriID, bBilgiID, bDeger) VALUES(@stkID, @bBilgiID, '0')" +
" END" +
" ELSE" +
" BEGIN" +
" UPDATE urnBilgi SET bDeger =" +
" CAST(CONVERT(decimal(9, 4), ((((Select fiyatS from urn where stkID = @stkID) + @eklenecekfiyat)) *" +
" CONVERT(decimal(9, 4), convert(varchar, '1.' + REPLACE(@komisyon, '.', ''))) ))AS varchar)" +
" FROM urnBilgi" +
" INNER JOIN" +
" urn ON urn.stkID = bVeriID" +
" WHERE bVeriID = @stkID and bBilgiID = @bBilgiID AND(urn.fiyatS BETWEEN @fiyat_baslangic and @fiyat_bitis)" +
" END" +
" END"
);
using (SqlConnection connection = new SqlConnection(constring))
{
using (SqlCommand cmd = new SqlCommand(sbSP.ToString(), connection))
{
connection.Open();
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
connection.Close();
}
}
}
Caius Jard's 目前没问题。虽然真正的问题没有解决,但我跳过了程序中的下一步。
You don't need to check the existence of your stored proc if you're running from c#; if the proc exists the attempt to create it will fail, and c# will experience an exception that you can ignore. Keep it simple
您不能在 SqlCommand
的 SQL 批处理中使用 GO
。它无效 T-SQL,它只是 sqlcmd 和 SSMS 使用的批处理分隔符。
相反,您需要使用新命令拆分批次。
但是在这个特定的实例中,您实际上并不需要两个命令。首先,因为您可以使用 CREATE OR ALTER PROCEDURE
组合它们。其次,即使您确实需要,因为您只是通过动态 SQL 执行此操作,您可以有条件地构建一个动态 SQL 脚本来传递。
您也可以删除 StringBuilder
并使用逐字 @""
字符串。这允许您插入换行符并充分利用空格。
您当前脚本的另一个问题是您没有为动态 SQL 转义 '
。像我一样将它作为参数传递可以避免这种情况,但是如果你想在一个大批量中完成所有操作,你需要确保这样做。
public void createSP()
{
const string sqlExec = @"
IF NOT EXISTS (SELECT 1
FROM sys.procedures
WHERE name = 'etradeCore')
exec(@sql);
";
const string sbSP = @"
CREATE OR ALTER PROCEDURE [dbo].[etradeCore]
@pazaryeri VARCHAR(50),
@magaza VARCHAR(50),
@stkID int,
@komisyon decimal(9, 4),
@fiyat_baslangic decimal(9, 4),
@fiyat_bitis decimal(9, 4),
@eklenecekfiyat decimal(9, 4)
AS
SET NOCOUNT OFF;
DECLARE @bBilgiID tinyint =
CASE @pazaryeri
WHEN 'Trendyol'
THEN
CASE @magaza
WHEN 'Kozmeti'
THEN 58
WHEN 'Golden Rose'
THEN 52
WHEN 'Ziaja'
THEN 60
END
WHEN 'Hepsiburada'
THEN
CASE @magaza
WHEN 'Kozmeti'
THEN 43
WHEN 'Golden Rose'
THEN 44
WHEN 'Ziaja'
THEN 43
END
END;
IF NOT EXISTS (SELECT 1
FROM urnBilgi
WHERE bVeriID = @stkID
AND bBilgiID = @bBilgiID
AND bDeger >= '0')
BEGIN
INSERT INTO urnBilgi (bVeriID, bBilgiID, bDeger)
VALUES (@stkID, @bBilgiID, '0');
END
ELSE
BEGIN
UPDATE b
SET bDeger = CAST(
CONVERT(decimal(9, 4),
(
u.fiyatS + @eklenecekfiyat
) * CONVERT(decimal(9, 4), '1.' + REPLACE(@komisyon, '.', ''))
) AS varchar(30))
FROM urnBilgi b
INNER JOIN urn u ON u.stkID = b.bVeriID
WHERE b.bVeriID = @stkID
AND b.bBilgiID = @bBilgiID
AND (u.fiyatS BETWEEN @fiyat_baslangic AND @fiyat_bitis);
END
";
using (SqlConnection connection = new SqlConnection(constring))
using (SqlCommand cmd = new SqlCommand(sqlExec, connection))
{
cmd.Parameters.Add("@sql", SqlDbType.NVarChar, -1).Value = sbSP;
connection.Open();
cmd.ExecuteNonQuery();
}
}
备注:
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
应该是连接字符串设置的一部分,不是批处理的一部分
- 使用
sys.procedures
代替sys.objects
- 使用
EXISTS (SELECT 1
代替EXISTS (SELECT *
- 不清楚为什么你将
@komisyon
作为小数传递只是为了删除 .
也许你应该传递 int
或者其他方式。
- 总是用长度声明
varchar
。
- 为什么
bDeger
还是 varchar
?不应该是decimal
吗?
- 不清楚为什么
UPDATE
中需要内部 SELECT fiyatS...
子查询而不仅仅是 u.fiyatS
.
CommandType.Text
是默认值。
- 如果您有
using
块(您应该这样做),则无需调用 Close()
。
- 为什么是
SET NOCOUNT OFF
?通常情况下,最好保留它。
- 整个“upsert”程序 should probably be in a transaction, with
HOLDLOCK
hints,可能应该使用 @@ROWCOUNT
来检查。
以下代码在 C# Winforms 中执行。我要做的是在程序启动时创建表和存储过程。我在 SQL 命令中使用 IF NOT EXISTS
。如果不存在,则创建,否则什么也不做。
创建表格没问题,效果很好。
但是在创建存储过程时,Visual Studio 抛出这个错误;
Must declare the scalar variable @xxxx
在收到此错误之前,我使用了 USE
和 GO
命令,这种方法也出现了 CREATE/ALTER PROCEDURE
错误。当我在网上研究时,我逐渐了解到 USE
和 GO
在 C# 中是禁忌。这就是为什么我在 IF NOT EXISTS
.
Else
而不是 GO
代码在 T-SQL 中有效,但我想我需要以某种方式将其转换为 C#。
感谢任何帮助。谢谢。
更新:
我把StringBuilder改成just string后,修复了之前的错误。现在它给出了下面的一个。虽然找不到任何问题。
" System.Data.SqlClient.SqlException
HResult=0x80131904
Message=Incorrect Syntax near the 'Trendyol'.
Unclosed quotation mark after the character string "', ')))))AS varchar) FROM urnBilgi INNER JOIN urn ON urn.stkID = bVeriID WHERE bVeriID = @stkID and bBilgiID = @bBilgiID AND(urn.fiyatS BETWEEN @fiyat_baslangic and @fiyat_bitis) END END'"
public void createSP()
{
StringBuilder sbSP = new StringBuilder();
sbSP.AppendLine(" IF NOT EXISTS(SELECT * FROM sys.objects WHERE type = 'P' AND object_id = object_id('etradeCore'))" +
" exec('CREATE PROCEDURE [dbo].[etradeCore](" +
" ELSE"+ //Used Instead of GO
" ALTER PROCEDURE [dbo].[etradeCore](" +
" @pazaryeri VARCHAR(50)," +
" @magaza VARCHAR(50)," +
" @stkID int," +
" @komisyon decimal(9, 4)," +
" @fiyat_baslangic decimal(9, 4)," +
" @fiyat_bitis decimal(9, 4)," +
" @eklenecekfiyat decimal(9, 4)" +
")" +
" AS BEGIN" +
" SET NOCOUNT OFF" +
" SET ANSI_NULLS ON" +
" SET QUOTED_IDENTIFIER ON" +
" DECLARE @bBilgiID tinyint" +
" IF(@pazaryeri = 'Trendyol' and @magaza = 'Kozmeti')" +
" SET @bBilgiID = 58" +
" ELSE IF(@pazaryeri = 'Trendyol' and @magaza = 'Golden Rose')" +
" SET @bBilgiID = 52" +
" ELSE IF(@pazaryeri = 'Trendyol' and @magaza = 'Ziaja')" +
" SET @bBilgiID = 60" +
" ELSE IF(@pazaryeri = 'Hepsiburada' and @magaza = 'Kozmeti')" +
" SET @bBilgiID = 43" +
" ELSE IF(@pazaryeri = 'Hepsiburada' and @magaza = 'Golden Rose')" +
" SET @bBilgiID = 44" +
" ELSE IF(@pazaryeri = 'Hepsiburada' and @magaza = 'Ziaja')" +
" SET @bBilgiID = 43" +
" IF NOT EXISTS(select * from urnBilgi WHERE bVeriID = @stkID and bBilgiID = @bBilgiID and bDeger >= convert(varchar, 0))" +
" BEGIN" +
" INSERT INTO urnBilgi(bVeriID, bBilgiID, bDeger) VALUES(@stkID, @bBilgiID, '0')" +
" END" +
" ELSE" +
" BEGIN" +
" UPDATE urnBilgi SET bDeger =" +
" CAST(CONVERT(decimal(9, 4), ((((Select fiyatS from urn where stkID = @stkID) + @eklenecekfiyat)) *" +
" CONVERT(decimal(9, 4), convert(varchar, '1.' + REPLACE(@komisyon, '.', ''))) ))AS varchar)" +
" FROM urnBilgi" +
" INNER JOIN" +
" urn ON urn.stkID = bVeriID" +
" WHERE bVeriID = @stkID and bBilgiID = @bBilgiID AND(urn.fiyatS BETWEEN @fiyat_baslangic and @fiyat_bitis)" +
" END" +
" END"
);
using (SqlConnection connection = new SqlConnection(constring))
{
using (SqlCommand cmd = new SqlCommand(sbSP.ToString(), connection))
{
connection.Open();
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
connection.Close();
}
}
}
Caius Jard's
You don't need to check the existence of your stored proc if you're running from c#; if the proc exists the attempt to create it will fail, and c# will experience an exception that you can ignore. Keep it simple
您不能在 SqlCommand
的 SQL 批处理中使用 GO
。它无效 T-SQL,它只是 sqlcmd 和 SSMS 使用的批处理分隔符。
相反,您需要使用新命令拆分批次。
但是在这个特定的实例中,您实际上并不需要两个命令。首先,因为您可以使用 CREATE OR ALTER PROCEDURE
组合它们。其次,即使您确实需要,因为您只是通过动态 SQL 执行此操作,您可以有条件地构建一个动态 SQL 脚本来传递。
您也可以删除 StringBuilder
并使用逐字 @""
字符串。这允许您插入换行符并充分利用空格。
您当前脚本的另一个问题是您没有为动态 SQL 转义 '
。像我一样将它作为参数传递可以避免这种情况,但是如果你想在一个大批量中完成所有操作,你需要确保这样做。
public void createSP()
{
const string sqlExec = @"
IF NOT EXISTS (SELECT 1
FROM sys.procedures
WHERE name = 'etradeCore')
exec(@sql);
";
const string sbSP = @"
CREATE OR ALTER PROCEDURE [dbo].[etradeCore]
@pazaryeri VARCHAR(50),
@magaza VARCHAR(50),
@stkID int,
@komisyon decimal(9, 4),
@fiyat_baslangic decimal(9, 4),
@fiyat_bitis decimal(9, 4),
@eklenecekfiyat decimal(9, 4)
AS
SET NOCOUNT OFF;
DECLARE @bBilgiID tinyint =
CASE @pazaryeri
WHEN 'Trendyol'
THEN
CASE @magaza
WHEN 'Kozmeti'
THEN 58
WHEN 'Golden Rose'
THEN 52
WHEN 'Ziaja'
THEN 60
END
WHEN 'Hepsiburada'
THEN
CASE @magaza
WHEN 'Kozmeti'
THEN 43
WHEN 'Golden Rose'
THEN 44
WHEN 'Ziaja'
THEN 43
END
END;
IF NOT EXISTS (SELECT 1
FROM urnBilgi
WHERE bVeriID = @stkID
AND bBilgiID = @bBilgiID
AND bDeger >= '0')
BEGIN
INSERT INTO urnBilgi (bVeriID, bBilgiID, bDeger)
VALUES (@stkID, @bBilgiID, '0');
END
ELSE
BEGIN
UPDATE b
SET bDeger = CAST(
CONVERT(decimal(9, 4),
(
u.fiyatS + @eklenecekfiyat
) * CONVERT(decimal(9, 4), '1.' + REPLACE(@komisyon, '.', ''))
) AS varchar(30))
FROM urnBilgi b
INNER JOIN urn u ON u.stkID = b.bVeriID
WHERE b.bVeriID = @stkID
AND b.bBilgiID = @bBilgiID
AND (u.fiyatS BETWEEN @fiyat_baslangic AND @fiyat_bitis);
END
";
using (SqlConnection connection = new SqlConnection(constring))
using (SqlCommand cmd = new SqlCommand(sqlExec, connection))
{
cmd.Parameters.Add("@sql", SqlDbType.NVarChar, -1).Value = sbSP;
connection.Open();
cmd.ExecuteNonQuery();
}
}
备注:
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
应该是连接字符串设置的一部分,不是批处理的一部分- 使用
sys.procedures
代替sys.objects
- 使用
EXISTS (SELECT 1
代替EXISTS (SELECT *
- 不清楚为什么你将
@komisyon
作为小数传递只是为了删除.
也许你应该传递int
或者其他方式。 - 总是用长度声明
varchar
。 - 为什么
bDeger
还是varchar
?不应该是decimal
吗? - 不清楚为什么
UPDATE
中需要内部SELECT fiyatS...
子查询而不仅仅是u.fiyatS
. CommandType.Text
是默认值。- 如果您有
using
块(您应该这样做),则无需调用Close()
。 - 为什么是
SET NOCOUNT OFF
?通常情况下,最好保留它。 - 整个“upsert”程序 should probably be in a transaction, with
HOLDLOCK
hints,可能应该使用@@ROWCOUNT
来检查。