【问题标题】:C# SQL loop in insert statement from string values从字符串值插入语句中的 C# SQL 循环
【发布时间】:2018-11-23 08:49:03
【问题描述】:

我有一个插入语句,它在表中创建一个新行:

string SQLQuery = "INSERT INTO TABLEABC VALUES('" + Rtext + "', '" + TDate + "')";

但我想为 8 周(56 天)间隔的 20 次重复数据创建多行。

例如:

Rtext TDate
John  23/11/2018

我希望它创建:

Rtext TDate
John  23/11/2018
John  18/01/2019
John  15/03/2019
John  10/05/2019
John  05/07/2019
John  30/08/2019
John  25/10/2019
John  20/12/2019
John  14/02/2020
John  10/04/2020
John  05/06/2020
John  31/07/2020
John  25/09/2020
John  20/11/2020
John  15/01/2021
John  12/03/2021
John  07/05/2021
John  02/07/2021
John  27/08/2021
John  22/10/2021
John  17/12/2021

【问题讨论】:

  • 递归 cte?每次迭代增加 56 天。
  • 只在 1 到 20 的循环中使用 DateTime.AddDays(56) 有什么问题?
  • 使用TDate.AddDays(56) 并为每次执行分配参数。不要忘记使用Parameters.Clear()。
  • @jarlh 是这样吗? DECLARE @i int = 0 WHILE @i < 300 BEGIN SET @i = @i + 1 /* your code*/ END
  • 你为什么不参数化你的查询呢?这对 SQL 注入很开放。就个人而言,我也会处理 SQL Server 中的其他行;通过使用 rCTE(如@jarlh 所述)或(虚拟)计数表。

标签: c# sql sql-server loops


【解决方案1】:

首先,您应该对查询进行参数化。我的 C# 非常生锈(很差),所以如果这是错误的,你将不得不原谅我,但是,如果我记得(并且我的 Google-fu 工作),那么你会想做更多类似的事情:

string SQLQuery = "INSERT INTO TABLEABC VALUES(@RText, @TDate)";
SQLCommand Command = new SQLCommand(SQLQuery, YourConnection);
Command.Parameters.Add("@RText",SQLDbType.varchar);
Command.Parameters["@RText"].Value = RText; //Assumed variable name
Command.Parameters.Add("@TDate",SQLDbType.date); //Assumed datatype
Command.Parameters["@TDate"].Value = TDate; //Assumed variable name

但这并不能解决重复项,因此第一行可以替换为:

string SQLQuery = "INSERT INTO TableABC SELECT @RText, DATEADD(WEEK, 8*I, @TDate) FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19)) V(I);";

或者,您可以使用 rCTE 代替虚拟 Tally 表。在 SQL 中,这看起来像:

WITH rCTE AS(
        SELECT @RText AS RText,
               @TDate AS TDate
               0 AS I
        UNION ALL
        SELECT RText,
               DATEADD(WEEK, (I+1)*8,TDate) AS TDate,
               I+1
        FROM rCTE
        WHERE I+1 <20
)
INSERT INTO TABLEABC
SELECT RText,
       TDate
FROM rCTE;

如果您要拥有大量值,可扩展的 Tally Table 是您的最佳选择:

WITH N AS(
        SELECT N
        FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
    FROM N N1 -10
         CROSS JOIN N N2 --100
         CROSS JOIN N N2 --1000
         CROSS JOIN N N2 --10000 --you get the idea
    )
INSERT INTO TABLEABC
SELECT TOP 500
       @RText,
       DATEADD(WEEK, (I-1)*8, @TDate)
FROM Tally;

在上述意义上,rCTE 是一种 RBAR 方法,因此您需要的行数越多,它的速度就会越慢。计数表会快得多,不是 RBAR,也不需要 MAXRECURSION 选项。

【讨论】:

  • 虽然这可能是正确的,但如果我们想插入 500 个值而不是 20 个值,您会这样做吗?
  • @Rafalon OP 在他们的帖子中明确指出 20。对于 500,我会使用可扩展的计数表。
  • 别生气,我的问题只是为了让您进一步思考并改进您(已经很好)的答案:)
  • 为你添加了一个可扩展的虚拟计数表@Rafalon
  • 你能把这个变成一个完整的答案吗?我不知道哪些部分需要使用
【解决方案2】:

查看您正在使用的语法,似乎您正在 C# 中尝试它。所以使用下面的代码。

您需要使用 stringbuilder 并在 SQLQuery 中附加插入字符串或连接。使用下面的代码。

Datetime myDate = Convert.toDatetime("23/11/2018")
string SQLQuery = "INSERT INTO TABLEABC VALUES('" + Rtext + "', '" + TDate + "');";
SQLQuery += "INSERT INTO TABLEABC VALUES('" + John  + "', '" + myDate + "');";

for (int i = 1; i <=19; i++)
{
      SQLQuery +=" INSERT INTO TABLEABC VALUES('" + John  + "', myDate.AddDays(56) );";
}

【讨论】:

  • 注入在这里还是个问题。
  • 你用什么注入数据库?是 linq 还是实体关系映射?
  • Rtext = "1','19000101'); DROP TABLE TABLEABC;--" 怎么样?
  • 是 Rtext 列的名称吗?什么是“19000101”?
  • @AgustusCodes 当你的答案中有Rtext 时,你不知道它是什么这一事实会让你的答案无效
【解决方案3】:

我更喜欢使用 SQL 参数和 for 循环为每次迭代执行插入命令,但您应该在下一次执行开始之前清除参数。这是一个假设您首先将日期字符串转换为DateTime 的示例:

string SQLQuery = "INSERT INTO TABLEABC (RText, TDate) VALUES (@RText, @TDate)";

// edit: TDate is a string, convert it to DateTime first
DateTime date;

if (DateTime.TryParseExact(TDate, "dd/MM/yyyy", CultureInfo.InvariantCulture, 
    DateTimeStyles.None, out date)
{
    using (var con = new SqlConnection(connectionString))
    {
        con.Open();
        using (var cmd = new SqlCommand(SQLQuery, con))
        {
            for (var i = 0; i < 20; i++)
            {
                cmd.Parameters.AddWithValue("@RText", Rtext);

                // add for next iterations
                if (i > 0)
                {
                    date = date.AddDays(56);
                }

                cmd.Parameters.AddWithValue("@TDate", date);

                cmd.ExecuteNonQuery();
                cmd.Parameters.Clear(); // clear existing parameters for next iteration
            }
        }
    }
}
else
{
    // handle invalid dates
}

注意:如果确切知道数据类型,请使用Parameters.Add() 而不是Parameters.AddWithValue()。

【讨论】:

  • 遇到错误:'string' does not contain a definition for 'AddDays' and no accessible extension method 'AddDays' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
  • 因为您将日期作为字符串存储在TDate 中?
  • 是string TDate= TDateTB.Text;
  • AddDays 用于DateTime。如果要将字符串转换为 DateTime,请使用 var date = DateTime.ParseExact(TDate, "dd/MM/yyyy", CultureInfo.InvariantCulture) 并在 Add()/AddWithValue() 中使用 date。
  • 嗯,现在得到The name 'CultureInfo' does not exist in the current context
【解决方案4】:

你可以试试这个。

DECLARE @i INT=0, @LastDate AS DATE=GETDATE()

WHILE (@i < 20)
BEGIN
    INSERT INTO TABLEABC VALUES('" + Rtext + "', '" + @LastDate + "')

    SET @LastDate = @LastDate.AddDays(56)
    SET @i=@i+1;
END

【讨论】:

  • 这不会按原样工作,您必须考虑它在 c# 代码中的事实。仔细检查你的引号;)这也不会阻止 SQL 注入。另外,SQL 真的支持.AddDays(56)吗?
  • SQL Server 内部循环不是很好,这根本不能解决注入问题。
  • AddDays(56) 在SET @LastDate = @LastDate.AddDays(56) 中做了什么?这是 SQL Server 中的新功能吗?
  • @Sami '@LastDate 的初始值是今天的日期。 '@LastDate.AddDays(56) 意味着我们将 56 天添加到 '@LastDate。
  • @ThilinaNakkawita,如果您在答案中得到的代码是支持该语法的语言,那就太好了。你在这里得到的是一个奇怪的 C# 和 SQL 的混合体,它不会在两者中运行
猜你喜欢
  • 1970-01-01
  • 2014-02-28
  • 1970-01-01
  • 1970-01-01
  • 2016-12-30
  • 2011-07-04
  • 2015-06-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多