Update multiple rows with WHERE clause - sql-server-2005

I am trying to update multiple rows at once (SQL Server 2005). Previously I was updating a single row with this query:
UPDATE dbo.My_Users
SET MyUserId = #MyUserId
WHERE EmailID = #EmailId
Now #EmailId will have comma-separated EmailIds.
How can I modify the script to update multiple rows? I have seen some examples which make use of UNION ALL. But they are mostly to insert multiple records without where clause.

A similar question was answered in Parameterize an SQL IN clause
The same idea can be applied here:
declare #EmailIds varchar = '|email1#test.com|email2#test.com|';
UPDATE dbo.My_Users SET MyUserId=#MyUserId WHERE #EmailIds LIKE '%|' + EmailID + '|%';
Though this does not contain a comma-separated list, the delimiter could easily be changed to a pipe-character. The caveat here is, the more data that exists in the table and the more email addresses that are in the #EmailIds list, the slower (much slower) this query can become.
Using C#, I would actually recommend the second example in the above-mentioned question where the list is expanded to create a query similar to:
UPDATE dbo.My_Users SET MyUserId=#MyUserId WHERE EmailID IN (#email1, #email2);
C# to implement (a modified version of the example in the question above):
string[] emails = new string { "email1#test.com", "email2#test.com" };
string sql = "UPDATE dbo.My_Users SET MyUserId=#MyUserId WHERE EmailID IN ({0});"
string[] emailParams = emails.Select((s, i) => "#email" + i.ToString()).ToArray();
string inClause = string.Join(",", emailParams);
using (SqlCommand cmd = new SqlCommand(string.Format(sql, inClause))) {
for(int i = 0; i < emailParams.Length; i++) {
cmd.Parameters.AddWithValue(emailParams[i], emails[i]);
}
}

You could use dynamic SQL
exec('UPDATE dbo.My_Users SET MyUserId = ' + cast(#MyUserId as varchar) + ' WHERE EmailID in (' + #EmailIds + ')')

Related

SqlCommmand parameters not adding to UPDATE statement (C#, MVC)

If you look at the stuff commented out, I can easily get this to work by adding user input directly in to the query, but when I try to parameterize it, none of the values are being added to the parameters...
This code is throwing an error
Must define table variable #formTable
but the issue is none of the values are adding, not just the table variable (verified by replacing table name variable with static text).
I have many insert statements in this project structured exactly like this one which work perfectly. What am I doing wrong here?
string constr = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr))
{
//string query = "UPDATE " + s.formTable + " SET " + s.column + " = '" + s.cellValue + "' WHERE MasterID = '" + s.id + "'";
string query = "UPDATE #formTable SET #column = #cellValue WHERE MasterID = #id;";
using (SqlCommand cmd = new SqlCommand(query))
{
//SqlParameter param = new SqlParameter("#formTable", s.formTable);
//cmd.Parameters.Add(param);
cmd.Parameters.AddWithValue("#formTable", s.formTable);
cmd.Parameters.AddWithValue("#column", s.column);
cmd.Parameters.AddWithValue("#cellValue", s.cellValue.ToString());
cmd.Parameters.AddWithValue("#id", s.id.ToString());
cmd.Connection = con;
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
Parameters are for values, not object identifiers (tables, columns, etc.), so the only valid parameters you have are #cellValue and #id.
If you want to dynamically set table/column names based on user input, you're likely looking at string concatenation. However, that doesn't necessarily mean SQL injection. All you need to do is validate the user input against a set of known values and use the known value in the concatenation.
For example, suppose you have a List<string> with all of your table names. It can be hard-coded if your tables are never going to change, or you can make it more dynamic by querying some system/schema tables in the database to populate it.
When a user inputs a value for a table name, check if it's in the list. If it is, use that matching value from the list. If it isn't, handle the error condition (such as showing a message to the user). So, even though you're using string concatenation, no actual user input is ever entered into the string. You're just concatenating known good values which is no different than the string literals you have now.

Query to retrieve all row data for supplied column name

I am using Eclipse and Oracle SQL Developer. My connections are all set up. I am trying to query my database in SQL Developer by passing in a column name as a variable.
For example, I just want to use something similar to this statement:
select * from CUSTOMERS;
but allow CUSTOMERS to be a variable where I can pass in any table name.
Currently this pulls all column names from given column name and connection:
final String query = "select column_name from all_tab_columns"
+" where owner = ?"
+" and table_name = ?";
try {
headers = DAO.useJNDI(jndi)
.setSQL(query)
.input(1, host)
.input(2, tableName)
.list(String.class);
I want to do the same thing but with rows. Does anyone know how to do this? This is what I am thinking about so far:
final String sql = "select *"
+ " from table_name"
+ " where owner = ? and table_name = ?";
try {
logger.debug(tableName+sourceJNDI);
sourceList = DAO.useJNDI(sourceJNDI)
.setSQL(sql)
.input(1, host)
.input(2, tableName)
.list(DatabaseCompareDto.class);
The main focus is the SQL statements. I know everything else works.
If I'm reading your question correctly, I think what you want is to replace the first table_name in your SQL with ?, then add an additional .input( 1, tableName) :
final String sql = "select *"
+ " from ?"
+ " where owner = ? and table_name = ?";
try {
logger.debug(tableName+sourceJNDI);
sourceList = DAO.useJNDI(sourceJNDI)
.setSQL(sql)
.input(1, tableName)
.input(2, host)
.input(3, tableName)
.list(DatabaseCompareDto.class);
You can't pass the table name as a parameter. Instead of wasting your energy on such an alleged generic solution, use or create a small templating engine which allows you to replace the table name in your query before sending it to the database.

Converting Sql query (update table set ....) to linq ( var m = ...)

I am trying to write a linq query in my Database.cs file. I find it difficult. Is there any tool to convert sql to linq? I tried linqer but it is no good. Or could you help me in writing the following query in linq.
update table
set field1='R',
field2='" + DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss") + "',
field3 = '" + util.CleanStringInput(value1) + "'
where field1 = 'P'
and field3 = '" + value2 + "'
and field4 = (select max(field5)
from table2
where field6='" + value2 + "')
The easiest way would be to fetch the entities, set the properties and then save changes:
var maxFromTable2 = context.YourTables2.
Where(t2 => t2.field6 == value2).
Max(t2 => t2.field5);
var entitiesToUpdate = context.YourTables.
Where(t => t.field1 == "P" &&
t.field3 == value2 &&
t.field4 == maxFromTable2).
ToList();
foreach (var entityToUpdate in entitiesToUpdate)
{
entityToUpdate.field1 = "R";
entityToUpdate.field2 = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss");
}
context.SaveChanges();
NOTE: It is not clear from your question what table you are updating, so I assume by default that it is a table different from table2. It could help if you indicate whether you are using LINQ to SQL or Entity Framework (LINQ to Entitites). The current syntax is for EF.
LINQ to SQL/EF are intended to hydrate objects and operate on those, saving the changes. It is not intended as a replacement for batch operations. If you use EF/LINQ to SQL in this case, you will be issuing n+1 requests to the database: 1 to select the records you are going to change and a separate request for each row (object) you are updating. With a small data set, this may be managable, but if you have any kind of volume, keeping this in a stored proc using a single Update statement may be a better option.

Dynamically add parameter to a 'IN' clause in a query?

I am attempting to query an MS Access DB with C#
Static query is -
Select FieldID , CDGroups.CDGroupID , Priority
from Fields , CDGroups
where Fields.CDGroupID = CDGroups.CDGroupID
and Fields.FieldID in ('f1','f2','f3')
order by Priority
I need to replace f1,f2.. FieldID from fieldIdList(List<string> fieldIdList) which contains all these fields
How can I go about it?
The correct way would be to use SQL parameters.
To do this, you need to loop through your list and create one parameter per list item - in the SQL string and in your query's list of SQL parameters.
Take a look at this answer to a similar question:
How to pass sqlparameter to IN()?
var listId= string.Join(",", fieldIdList.Select(m => string.Format("'{0}'", m)));;
then
"Field.FieldID in (" + listId+ ")
I used following code:
StringBuilder sb = new StringBuilder();
foreach (string fieldId in fieldIdList)
{
sb.Append("'" + fieldId + "',");
}
string fieldList = sb.ToString().TrimEnd(',');
string queryString =
"Select FieldID , CDGroups.CDGroupID , Priority from Fields , CDGroups where Fields.CDGroupID = CDGroups.CDGroupID and Fields.FieldID in (" + fieldList + ") order by Priority";
Try this query -
SELECT CDGroupID FROM fields
WHERE FieldID IN ('f1', 'f2', 'f3')
GROUP BY CDGroupID
HAVING(COUNT(DISTINCT FieldID)) >= 3
It will show a list of CDGroupID that have 'f1','f2' and 'f3' at least. If you want to show ones that have just 'f1','f2' and 'f3', then change WHERE condition to 'HAVING(COUNT(DISTINCT FieldID)) >= 3'.

Keyword search to include Uniqueidentifier field

I'm wanting to let a user search rows in a database by specifying a keyword to look for. There are a few fields I would like to look in for this keyword, one of which is a uniqueidentifier. The problem is, if the keyword is not a GUID, I get the following error message:
Conversion failed when converting from a character string to uniqueidentifier
The SQL I'm using to run the search looks something like this:
// do not use
string sql = #"SELECT *
FROM [MyTable]
WHERE [MyTable].[TableID] = '" + keyword + "'";
WARNING: this is just example code - DO NOT write sql commands like this as it creates a security risk
How do I write my SQL select statement such that it will not fail when keyword is not a GUID?
string sql;
Guid id;
if (Guid.TryParse(keyword, out id))
{
sql = #"SELECT *
FROM [MyTable]
WHERE [MyTable].[TableID] = '" + keyword + "'";
}
else
{
sql = //search by other way
}
Does this work for you?
string sql = #"SELECT *
FROM [MyTable]
WHERE convert(varchar,[MyTable].[TableID]) = '" + keyword + "'";
I know this doesn't really help you today, but may help future readers. In SQL Server 2012 you will be able to use TRY_CONVERT:
string sql = #"SELECT *
FROM dbo.[MyTable]
WHERE [TableID] = TRY_CONVERT(UNIQUEIDENTIFIER, '" + keyword + "');";
But what you really should be doing is passing the parameter as a strongly typed GUID, and handling the error (using try/catch) in the client program when someone enters something that isn't a GUID.