0

I have an insert statement which creates a new row into a table:

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

But I want to create multiple lines for 20 repeats worth of data for 8 week (56 days) intervals.

For example:

Rtext TDate
John  23/11/2018

I would want it to create:

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
Matt
  • 14,906
  • 27
  • 99
  • 149

4 Answers4

2

Firstly, you should be parametrising your query. My C# is very rusty (poor), so you'll have to excuse me if this is wrong, however, if I recall (and my Google-fu worked), then you'll want to do something more like:

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

This doesn't solve the repeating items though, so the first line could be replaced with:

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);";

Alternativey, you could use a rCTE instead of a virtual Tally table. In SQL this would look like:

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;

If you're going to have a large amount of values, a scalable Tally Table is the way to go:

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;

A rCTE, in the sense above, is an RBAR method, so it's speed will get slower the more rows you need. A tally table would be far faster, isn't RBAR, and doesn't need the MAXRECURSION option.

Thom A
  • 88,727
  • 11
  • 45
  • 75
  • 1
    While this may be correct, would you do the same if we wanted to insert 500 values instead of 20? – Rafalon Nov 23 '18 at 09:09
  • @Rafalon The OP specifically states 20 in their post. For 500 I would use a scalable tally table,. – Thom A Nov 23 '18 at 09:11
  • Don't take offense, my question was only here to make you think further and improve your (already good) answer :) – Rafalon Nov 23 '18 at 09:12
  • Added a Scalable Virtual Tally Table for you @Rafalon – Thom A Nov 23 '18 at 09:16
  • Can you put this into a full answer? I dont know which parts need using – Matt Nov 23 '18 at 10:53
  • @Matt My C# is very basic I'm afraid; what I wrote in c# was based off the documentation ([SqlCommand.Parameters Property](https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.parameters?view=netframework-4.7.2)), I'm a SQL Developer/DBA first, and I have little exposure to C# apart from when i'm working with the web/application developers. There should, however, be more than enough information above to get you where you need to be. – Thom A Nov 23 '18 at 11:08
1

Looking into the syntax you are using, seems like you are trying it in C#. So use below code.

you need to use stringbuilder and append insert string or concate in SQLQuery. Use below code.

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) );";
}
Agustus Codes
  • 57
  • 1
  • 10
  • Injection is still a problem here. – Thom A Nov 23 '18 at 09:00
  • what are you using for injecting in database? is it linq or entity-relational mapping? – Agustus Codes Nov 23 '18 at 09:03
  • How about `Rtext = "1','19000101'); DROP TABLE TABLEABC;--"`? – Thom A Nov 23 '18 at 09:04
  • Is Rtext name of column ? what is '19000101'? – Agustus Codes Nov 23 '18 at 09:05
  • @AgustusCodes the very fact that you do not know what `Rtext` is when you have it in your answer kind of invalidates your answer – Rafalon Nov 23 '18 at 09:07
  • you have to create a string in such a way that if you execute it in sql editor, it will work. Then you can inject it from C# – Agustus Codes Nov 23 '18 at 09:07
  • Common man, seems like Rtext is a name of column and John is the records in it. My syntax is correct and should work if you know technical terms – Agustus Codes Nov 23 '18 at 09:08
  • `Rtext` is the name of the C# variable containing what is supposed to be the **value** for the first column of the `TABLEABC` SQL table – Rafalon Nov 23 '18 at 09:10
  • value of Rtext is always John so directly passing John in insert statement will not make difference.. – Agustus Codes Nov 23 '18 at 09:14
  • What Larnu said is that if an attacker types `"1','19000101'); DROP TABLE TABLEABC;--"` in the variable `Rtext`, you will end up dropping TABLEABC, this is why this approach is vulnerable to SQL-injection – Rafalon Nov 23 '18 at 09:24
  • Got it. Larnu this is the drawback of the way you are trying to insert into table and is inevitable if you decide to use it. Alternatively, you can use linq to sql to insert record that will prevent injection. – Agustus Codes Nov 23 '18 at 09:33
1

I preferred using SQL parameters and a for loop to execute the insert command for every iteration, but you should clear parameters before next execution begins. Here is an example by assumed that you're converting the date string to DateTime first:

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
}

Note: If the data type is exactly known, use Parameters.Add() instead of Parameters.AddWithValue().

Tetsuya Yamamoto
  • 24,297
  • 8
  • 39
  • 61
  • Getting an error: `'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?)` – Matt Nov 23 '18 at 09:24
  • Because you have date stored as string in `TDate` maybe? – Rafalon Nov 23 '18 at 09:25
  • Yes `string TDate= TDateTB.Text;` – Matt Nov 23 '18 at 09:26
  • `AddDays` is used for `DateTime`. If you want to convert a string to DateTime, use `var date = DateTime.ParseExact(TDate, "dd/MM/yyyy", CultureInfo.InvariantCulture)` and use `date` inside `Add()/AddWithValue()`. – Tetsuya Yamamoto Nov 23 '18 at 09:26
  • hmm getting `The name 'CultureInfo' does not exist in the current context` now – Matt Nov 23 '18 at 09:34
  • Add `using System.Globalization` namespace, or use fully-qualified name `System.Globalization.CultureInfo.InvariantCulture`. – Tetsuya Yamamoto Nov 23 '18 at 09:36
  • Ok getting closer! No more errors in the solution but when running the code I get `String was not recognized as a valid DateTime` – Matt Nov 23 '18 at 09:39
  • That's in `DateTime.ParseExact()`, right? If the textbox potentially contains any string which cannot be converted to `DateTime`, put `if` condition with `DateTime.TryParseExact()`. Which kind of string which not recognizable as `DateTime`? – Tetsuya Yamamoto Nov 23 '18 at 09:42
  • Sounds like the OP is using a simple text box for the date input, and not a datepicker. – Thom A Nov 23 '18 at 09:43
  • I put the datetime picker in a text box like so: `TDateTB.Text = TDateDateTimePicker.Text;` THEN `string TDate = TDateTB.Text;` – Matt Nov 23 '18 at 09:43
  • I assumed the datepicker accepts `dd/MM/yyyy` format as in your question, then `TryParseExact` should be used to prevent invalid date and store parseable date into `DateTime` field. – Tetsuya Yamamoto Nov 23 '18 at 09:48
  • new error : `Must declare the scalar variable "@RText".` – Matt Nov 23 '18 at 10:02
  • Check if `@RText` exists inside `cmd.Parameters` collection before `ExecuteNonQuery()`. If the parameter doesn't exist, probably `cmd.Parameters.Clear()` should be moved. Also worth to read: https://stackoverflow.com/questions/8747066/executenonquery-inside-loop. – Tetsuya Yamamoto Nov 23 '18 at 10:08
  • Did the first and subsequent loops reached the `cmd.Parameters.AddWithValue("@RText", Rtext)` line? How many iterations passed before the error started to appear? – Tetsuya Yamamoto Nov 23 '18 at 11:39
0

You can try this.

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
Dumi
  • 1,414
  • 4
  • 21
  • 41
  • 3
    This won't work as is, you have to consider the fact that it is inside c# code. Double-check your quotation marks ;) Also this won't prevent SQL-injection. Plus, does SQL really support `.AddDays(56)`? – Rafalon Nov 23 '18 at 08:59
  • Looping is not great inside SQL Server, and this doesn't address to injection issue at all. – Thom A Nov 23 '18 at 09:00
  • 1
    What does `AddDays(56)` do in `SET @LastDate = @LastDate.AddDays(56)`? is this a new function in SQL Server? – Ilyes Nov 23 '18 at 10:20
  • @Sami Initial value of '@LastDate is today's date. '@LastDate.AddDays(56) means we are adding 56days to the '@LastDate. – Dumi Nov 23 '18 at 10:59
  • @ThilinaNakkawita, that's great, if the code you've got in your answer was in a language that supported that syntax. What you've got here is a weird hybrid of C# and SQL, which will run in neither – Rob Nov 23 '18 at 11:26