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

在收到此错误之前,我使用了 USEGO 命令,这种方法也出现了 CREATE/ALTER PROCEDURE 错误。当我在网上研究时,我逐渐了解到 USEGO 在 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 来检查。