MySQL possible to update multiple rows with different where clause, single query - c#

Is is possible to have one query update multiple columns with different where clauses
for example, can you combine this?
update tblTest set price = '5000' where id = '2'
update tblTest set price = '3000' where id = '3'
update tblTest set price = '4000' where id = '4'
how would you combine those 3 queries to one query?

I think it's
UPDATE `tblTest` SET `price` = CASE `Id`
WHEN 2 THEN 5000
WHEN 3 THEN 3000
WHEN 4 THEN 4000
END CASE;

Related

Npgsql 5 a single select and multiple arguments

I use Npqsql 5. Is there any way to retrieve values for many different arguments using a single select?
For example I want to run:
"select value from table where id=#id1"
"select value from table where id=#id4"
"select value from table where id=#id12"
but as a single query in order to optimize.
Couple of options that come to mind:
select value from table where id = #id1 or id = #id4 or id = #id12
or
select value from table where id in (#id1, #id4, #id12)

MySQL IN clause: How can I get multiple rows when I use a same value multiple times?

I'm programming a C# Windows Forms Application in Visual Studio and I'm trying to get data about prices of products and the amount a user has added a product to its shopping list from my local MySQL-database into a List(int).
What I do is following:
If a user has added a product 4 times to their shopping list, I'm adding the barcode of the product 4 times to my List(int).
This is working but when I'm reading out all items of the List with the String.Join()-method into the IN-clause of my query and execute it, it only returns a row one time altough the IN-operator has the same barcode multiple times.
The following is how I'm adding barcodes to my List(int)
int count = 0;
List<int> barcodes = new List<int>();
MySqlCommand cmd = new MySqlCommand("SELECT product_barcode, amount FROM shopping_list_items WHERE shopping_list_id = " + current_shoppingListID + ";", db.connection);
cmd.ExecuteNonQuery();
var reader = cmd.ExecuteReader();
while (reader.Read())
{
do
{
barcodes.Add(Int32.Parse(reader["product_barcode"].ToString()));
count++;
} while (count < Int32.Parse(reader["amount"].ToString()));
}
reader.Close();
This is how I'm executing my query and assign the values to variables:
MySqlCommand cmdSum = new MySqlCommand("SELECT sum(price) AS 'total', supermarket_id FROM prices WHERE barcode IN (" + String.Join(", ", barcodes) + ") GROUP BY supermarket_id;", db.connection);
cmdSum.ExecuteNonQuery();
var readerSum = cmdSum.ExecuteReader();
while (readerSum.Read())
{
switch (double.Parse(readerSum["supermarket_id"].ToString()))
{
case 1:
sumSupermarket1 = double.Parse(readerSum["total"].ToString());
break;
case 2:
sumSupermarket2 = double.Parse(readerSum["total"].ToString());
break;
case 3:
sumSupermarket3 = double.Parse(readerSum["total"].ToString());
break;
}
}
A simplified query just to make it simple may look like this:
SELECT name FROM products WHERE barcode IN (13495, 13495, 13495);
If the above one is my query then I want it to return 3 the same rows.
So my question now is, how can I get multiple rows altough I use a same value multiple times in the IN-clause of a MySQL-query?
Q: how can I get multiple rows altough I use a same value multiple times in the IN-clause of a MySQL-query?
A: We don't. That's not how IN () works.
Note that
WHERE foo IN ('fee','fi','fi','fi')`
Is shorthand for
WHERE ( foo = 'fee'
OR foo = 'fi'
OR foo = 'fi'
OR foo = 'fi'
)
Understand what's happening here. MySQL is going to examine each row, and for each row it checks to see if this condition returns TRUE or not. If the row satisfies the condition, the row gets returned. Otherwise the row is not returned.
It doesn't matter that a row with foo value of 'fi' satisfies multiple conditions. All MySQL cares about is that the condition inside the parens ultimately evaluates to TRUE.
As an illustration, consider:
WHERE ( t.picked_by = 'peter piper'
OR t.picked_amount = 'peck'
OR t.name LIKE '%pickled%'
OR t.name LIKE '%pepper%'
)
There could be a row that satisfies every one of these conditions. But the WHERE clause is only asking if the entire condition evaluates to TRUE. If it does, return the row. If it doesn't, then exclude the row. We don't get four copies of a row because more than one of the conditions is satisfied.
So how do we get a set with multiple copies of a row?
As one possible option, we could use separate SELECT statements and combine the results with UNION ALL set operator. Something like this:
SELECT p1.name FROM product p1 WHERE p1.barcode IN (13495)
UNION ALL
SELECT p2.name FROM product p2 WHERE p2.barcode IN (13495)
UNION ALL
SELECT p3.name FROM product p3 WHERE p3.barcode IN (13495)
Note that the result from this query is significantly different than the result from the original query.
There are other query patterns that can return an equivalent set.
FOLLOWUP
Without an understanding of the use case, the specification, I'm just guessing at what we are attempting to achieve. Based on the two queries shown in the code (which follows a common pattern we see in code that is vulnerable to SQL Injection),
The shopping list:
SELECT i.product_barcode
, i.amount
FROM shopping_list_item i
WHERE i.shopping_list_id = :id
What is amount? Is that the quantity ordered? We want two cans of this, or three pounds of that? Seems like we would want to multiply the unit price by the quantity ordered to get the cost. (Two cans is going to cost twice as much as one can.)
If what we are after is the total cost of the items on the shopping list from multiple stores, we could do something like this:
SELECT SUM(p.price * s.amount) AS `total`
, p.supermarket_id
FROM ( SELECT i.product_barcode
, i.amount
FROM shopping_list_item i
WHERE i.shopping_list_id = :id
) s
JOIN price p
ON p.barcode = s.product_barcode
GROUP
BY p.supermarket_id
Note that if a particular product_barcode is not available for particular supermarket_id, that item on the list will be excluded from the total, i.e. we could get a lower total for a supermarket that doesn't have everything on our list.
For performance, we can eliminate the inline view, and write the query like this:
SELECT SUM(p.price * i.amount) AS `total`
, p.supermarket_id
FROM shopping_list_item i
JOIN price p
ON p.barcode = i.product_barcode
WHERE i.shopping_list_id = :id
GROUP
BY p.supermarket_id
If we absolutely have to rip through the shopping list query, and then use the rows from that to create a second query, we could form a query that looks something like this:
SELECT SUM(p.price * i.amount) AS `total`
, p.supermarket_id
FROM ( -- shopping_list here
SELECT '13495' AS product_barcode, '1'+0 AS amount
UNION ALL SELECT '13495', '1'+0
UNION ALL SELECT '13495', '1'+0
UNION ALL SELECT '12222', '2'+0
UNION ALL SELECT '15555', '5'+0
-- end shopping_list
) i
JOIN price p
ON p.barcode = i.product_barcode
WHERE i.shopping_list_id = :id
GROUP
BY p.supermarket_id
You would probably be better off investigating LINQ to SQL rather than using direct SQL and injection.
You can use an inline table join to accomplish what you want:
"SELECT sum(price) AS 'total', supermarket_id
FROM (select "+barcodes[0]+"as bc union all select "+String.Join(" union all select ", barcodes.Skip(1).ToArray())+") w
JOIN prices p ON p.barcode = w.bc
GROUP BY supermarket_id;"
Note: If you can name the column with the inline table alias (I couldn't test that) you could simplify the inline table generation.

Multiple IN Clauses in SQL

I'm not sure about the functionality of the IN clause so I want to know if I can run a query safely. For what I understand when using IN clause in SQL Server creates a series of OR statements:
So for example
SELECT * FROM table WHERE column IN (1, 2, 3, 4, 5, 6)
Will be the same that:
SELECT * FROM table WHERE column = 1 OR column = 2 OR column = 3 OR column = 4 OR column = 5 OR column = 6
But what happens when something like this is executed:
SELECT * FROM table WHERE column IN (1, 2, 3) and column2 IN (4,5,6) and column 3 IN (5,7,8)
Here I want to know if for example, if some value from the first IN has two occurrences within the second one it will retrieve both results, or if it's executed in order like 1 and 4 and 5 (The first value within the IN clauses)
I want to run a query with 8 parameters that I need to check in the DB (Sending by C#) but I don't want to build a super huge query with a lot of OR like this (because it will be +90k records)
SELECT * FROM table WHERE (column = 1 and column2 = 4 and column3 = 5 ) or
(column = 2 and column2= 5 and column3 = 7) OR ....
Or I don't know if there is a better approach to solve this, I'm open to ideas.
Thanks
The INs are separate conditions, so the logic is equivalent to:
where (column1 = 1 or column1 = 2 or column1 = 3) and
(column2 = 4 or column2 = 5 or column2 = 6)
Note that this is a logical equivalence. Some databases optimize IN with constant lists, for instance, by creating a binary tree for searching the values. I don't think that SQL Server does such an optimization though.
If you want "alignment", some databases -- but not SQL Server -- support tuples using IN:
where (column1, column2) in ( (1, 4), (2, 5), (3, 5) )
And this can always be represented as:
where (column1 = 1 and column2 = 4) or
(column1 = 2 and column2 = 5) or
(column1 = 3 and column2 = 6)

Compare two DataTables with several keys and select the rows that are not present in second table

I have two DataTables and I want to select the rows from the first one which are not present in second one, both tables have 3 Keys custnum, shiptonum, connum
For example:
Table Contacts
custnum shiptonum connum column
1 1 1 data1
2 2 2 data2
3 3 3 data3
4 4 4 data4
Table Invitations
custnum shiptonum connum column
1 1 1 data11
3 3 3 data33
I'd like the result to be:
Table Result
custnum shiptonum connum column
2 2 2 data2
4 4 4 data4
I already tried using
var differences = table1.AsEnumerable().Except(table2.AsEnumerable(),DataRowComparer.Default);
but it didn't work. For example in my testing in Contacts table I have 14,389 records, in Invitations table I have two records that exist in Contacts table the count after using the abovesolution was 14,389 instead of 14,387 (removing the two records from Invitations table).
You wrote:
I want to select the rows from the first one which are not present in second one
From your example, I see that you don't want to select rows from the first table that are not rows in the second table, but that you only want to take the values of the keys into account:
I want to select all rows from tableA which have keys with values that are not keys from tableB
You didn't define your tables. They might be IQueryable, or IEnumerable, for your LINQ statements there is not a big difference. Try to avoid AsEnumerable, especially if your data source is in a different process, like a database management system. The other process is much more efficient in executing your query than your process. AsEnumerable transports all data from your other process to your process, which is a relatively slow process. Therefore as a rule: Only use AsEnumerable this if you really need to,
The second definition defines clearer what you want: apparently from tableB you only need the keys:
var keysTableB = tableB.Select(row => new
{
CustNum = row.custNum,
ShipToNum = row.shiptonum,
ConNum = row.connum,
});
In words: from every row in tableB make one new object of anonymous type with three properties: CustNum, ShipToNum and ConNum
Select uses lazy execution. No query is executed, only property Expression is changed.
Now you want to keep only the rows from tableA that have a key that is a member of sequence keysTableB: if you want to keep a subset of a sequence, use Where
var result = tableA.Where(row => keysTableB.Contains(new
{
CustNum = row.custNum,
ShipToNum = row.shiptonum,
Connum = row.connum,
}));
In words: from every row in tableB keep only those rows that have a key that is also in keysTableB, using value equality.
TODO: consider concatenating these two LINQ statements into one.I doubt whether this would improve performance. It surely will deteriorate readability of your code, and thus decreases changeability / maintenance / testability.
for (int i=0;i<table1.rows.count;i++)
{
var rowExists = from dr in table2.AsEnumerable()
where dr.Field<typeofcolumn>("colum_name")==table1.Rows[i]["column_name"]
select dr;
if(rowExists.ToList().Count==0)
{
//here u import row table1.rows[i] to new table
}
}

How to get the count of occurrences in one table and update another table to that value?

I want to write an SQL statement that will:
Count the number of rows in the table booking that are open and where the booking.postcode is "MK"
Take a note of the plot (in booking.plot_id), and then update the table plot.jobs with the value count
For example running the SQL query when booking table has the following rows:
Would see the following highlighted values in plot.jobs being updated to 1:
This my code so far (note I am using Connector/Net):
public int CountBooking()
{
string query = "SELECT Count(*) FROM booking WHERE postcode=MK AND status=open";
int count = -1;
// ExecuteScalar will return one value
var cmd = new MySqlCommand(query, _connection);
count = int.Parse(cmd.ExecuteScalar() + "");
// Close
CloseConnection();
return count;
}
If there were 3 rows with a plot_id of 4, and 6 rows with a plot_id of 6, then the highlighted values will be updated to 3 and 6 respectively.
How would I go about achieving it?
You are close on the first part. If you want the number of openings for each plot_id, you'll need to use that in your GROUP BY statement like this:
SELECT plot_id, COUNT(*) AS numOpenings
FROM bookings
WHERE postcode = 'MK' AND status = 'open'
GROUP BY plot_id;
You can use that as a subquery in your UPDATE statement by joining it to the plot table and updating the matching rows, like this:
UPDATE plot p
JOIN(
SELECT plot_id, COUNT(*) AS numOpenings
FROM bookings
WHERE postcode = 'MK' AND status = 'open'
GROUP BY plot_id) temp ON temp.plot_id = p.plot_id
SET p.jobs = temp.numOpenings;
This worked out in SQL Fiddle. Let me know if you have more problems.
Never used Connector/Net but I would do something like this:
Run this query to retrieve the plot_id and JobCount:
select plot_id, count(*) JobCount from booking where postcode = 'mk' and status = 'open' group by plot_id;
Then scroll through this result set and issue update commands like this:
Update Jobs set jobs = [JobCount] where plot_ID = [plot_id]
Note the values of [JobCount] and [plot_id] will come from your first query.

Categories