I’m having some trouble with a t-sql select query in C#. The query is as follows:
@"SELECT * FROM
(SELECT *, ROW_NUMBER() OVER (ORDER BY id) as row_number
FROM [mytable]) t0
WHERE row_number BETWEEN @skip and @take
AND
uploadDate IN (@years)
AND NOT photoId IN (@shownPhotos)
ORDER BY NEWID()";
cmd.Parameters.Add("@skip", SqlDbType.Int).Value = skip;
cmd.Parameters.Add("@take", SqlDbType.Int).Value = take;
cmd.Parameters.Add("@shownPhotos", SqlDbType.VarChar).Value = shownPhotos;
cmd.Parameters.Add("@years", SqlDbType.VarChar).Value = years;
The method takes four parameters: skip count, take count, shownPhotos (commaseparated string which contains photo ids), years (comma separated string which contains the years to show the photos from)
So basically I want it to return photos from my database which matches any of the years from the commaseparated string and which are not already shown.
The problem (I think) is that my parameters are strings, so they are intepreted as ‘1234,567,890’ and ‘2011,2012’. When running the query in the SQL server management tool without the ‘s it works fine.
Anyone got a workaround for this? 🙂
Thanks a lot in advance!
Updated code:
// Create datatables
DataTable yearsTable = new DataTable();
yearsTable.Columns.Add("YearID", typeof(string));
yearsTable.Columns.Add("Year", typeof(string));
foreach(string year in years.Split(','))
yearsTable.Rows.Add(new object[] { year, year });
using (var conn = new SqlConnection(GlobalSettings.DbDSN))
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText =
@"SELECT * FROM
(SELECT *, ROW_NUMBER() OVER (ORDER BY id) as row_number
FROM [mytable]) t0
WHERE row_number BETWEEN @skip and @take
AND
uploadedDate IN (@years)
ORDER BY NEWID()";
SqlParameter skipParam = cmd.Parameters.AddWithValue("@skip", skip);
skipParam.SqlDbType = SqlDbType.Int;
SqlParameter takeParam = cmd.Parameters.AddWithValue("@take", take);
takeParam.SqlDbType = SqlDbType.Int;
SqlParameter yearsParam = cmd.Parameters.AddWithValue("@years", yearsTable);
yearsParam.SqlDbType = SqlDbType.Structured;
yearsParam.TypeName = "dbo.MyTableType";
var reader = cmd.ExecuteReader();
}
Indeed, your
INare being interpreted as “is in this set of 1 value”, i.e. what you have is identical to:evaluated as regular string equality on the strings
'1234,567,890'and'2011,2012'. At the TSQL level, a common trick is to write a “split” UDF that separates a single varchar into multiple row values. Many libraries have tools for this too; for example, in “dapper” we adding a syntax trick to pre-expand them, i.e.Note the lack of brackets next to
IN, which we interpret as “figure this out yourself” to the library, which then does exactly what you want. It will actually be evaluated as:where
@years0has the value2011,@years1has the value2012, etc.