Adapt result of one row to other rows - mysql

How can I can write the query this way, that the result of the CASE WHEN - statement is adapted to every row. So that in every row the result will be 5. Thank you very much!
CREATE TABLE DATA
(`Person` CHAR(4),
`Apples` INT(1),
`Tomatoes` INT(1),
`Result` INT(1)
);
INSERT INTO DATA
(Person, Apples, Tomatoes)
VALUES ('Mark' , 1, 2),
('Sepp', 2, 3),
('Carl', 3, 1);
UPDATE DATA
SET `Result` = CASE WHEN (`Person` = 'Sepp') THEN (`Apples` + `Tomatoes`) END;
Table of result as it should be
SQL fiddle demonstration

If you want all rows to get the value from Sepps row you can do it using a subquery.
The "normal" way would be to do this:
UPDATE DATA
SET Result = (SELECT Apples + Tomatoes FROM DATA WHERE Person = 'Sepp')
But this will most likely give you an error with MySQL (can't specify target table for update) and a workaround is to introduce another level in the query which forces a temporary table to be used, like this:
UPDATE DATA
SET Result = (
SELECT Value FROM (
SELECT Apples + Tomatoes AS Value
FROM DATA WHERE Person = 'Sepp'
) t
);

Related

SQL insert with empty value for the id

How do I insert a sql row into a new table where it meets criteria but resets the id value. In other words, copy the row, but reset the id value.
This is my current sql
INSERT INTO followers_lost SELECT * FROM followers WHERE pk = $pk
I tried to SET id=null and VALUE (0), but both don't work.
All you have to do since you want all the columns except the identity is specify all the non-identity columns on the insert:
INSERT INTO [followers_lost] ([Column1],[column2]...{but not the identity
column})
SELECT [Column1],[column2]...{but not the identity column} FROM followers WHERE
pk = $pk
You can create the column and then update it:
SET #new_id=0;
UPDATE your_table
SET id = #new_id := #new_id + 1
where id = 0
OK based on your comment I think you are trying to take all or some of the fields from Followers except the PK and put them into Followers_Lost where they equal a certain PK. If you want multiple PK's you would need to change the where clause to an IN statement instead of an equal and adjust your values accordingly.
CREATE TABLE dbo.UAT_Followers_Lost (PK INT IDENTITY(1,1),DATA VARCHAR(50) )
CREATE TABLE dbo.UAT_Followers (PK INT IDENTITY(1,1),DATA VARCHAR(50) )
INSERT INTO dbo.UAT_Followers
(DATA)
SELECT 'Jan'
UNION ALL
SELECT 'Feb'
UNION ALL
SELECT 'Mar'
DECLARE #PK INT
SET #PK = 1
INSERT INTO dbo.UAT_Followers_Lost
(Data)
SELECT Data
FROM dbo.UAT_Followers
WHERE PK = #PK

Mysql insert with value from target table

So I need to select a value from my table and modify it slightly to use as a value in a new row in the same table when inserting. See below:
INSERT INTO reservationbody(
reservationid,
driverid,
tripnumber,
fromlocation,
tolocation
)
VALUES(
1,
2,
(
SELECT
MAX(tripnumber) + 1
FROM reservationbody
WHERE reservationid = 1
),
'here',
'there'
)
I'm getting the following error:
You can't specify target table 'reservationbody' for update in FROM
clause
I've had a look at other questions and have found plenty of questions on this error but they all seem to be about updates and deletes and all require PK values that already exist. None deal with inserting.
Basically what I'm expecting here is that if SELECT MAX(tripnumber) FROM reservationbody where reservationid = 1 returns a value of 4 then the insert should add a new row with a tripnumber value of 5
Can anyone suggest how I can accomplish this?
Have you tried (I know you can do this in MS SQL):
INSERT INTO reservationbody(reservationid,driverid,tripnumber,
fromlocation,tolocation)
SELECT 1, 2, MAX(tripnumber) + 1, 'here', 'there'
FROM reservationbody
WHERE reservationid = 1

Updating Different Rows Quickly

I have a table which goes somewhat as follows:
Name1 (Key)|Name2(Key)|Total|LastUpdate
There will be hundreds of thousands of such records.
Now very frequently, my main program will query a source to get updated values. It will then update the total and last update. It may have to update hundreds of such rows.
Currently I have this:
Update mytable SET Total=[...] AND LastUpdate=[....] WHERE Name1='x' AND Name2='y';
Update mytable SET .....
I'm wondering if there is a faster way of updating the rows (similar to how you can Insert multiple rows at the same time).
The Totals will be completely different, but the LastUpdate time will be the same for each Update.
Update to Clarify:
The changes to total aren't just an increment, and don't depend on the current value - so its not deterministic in that regard. The source provides me a new value every second or so, and I have to put in a new one.
if its to slow to do the updates separatly, mayby you can do somthing like this
CREATE TABLE mytable_updates
(
Name1 VARCHAR(50) NOT NULL,
Name2 VARCHAR(50) NOT NULL,
total_increase INT NOT NULL
PRIMARY KEY(Name1, Name2)
)
INSERT INTO mytable_updates VALUES ('x', 'y', 5), ('x', 'z', 3);
...
UPDATE mytable_updates
LEFT JOIN mytable USING (Name1, Name2)
SET
Total = Total + total_increase,
LastUpdate = NOW();

A multitude of the same id in an WHERE id IN () statement

I have a simple query that increases the value of a field by 1.
Now I used to loop over all id's and fire a query for each of them, but now that things are getting a bit resource heavy I wanted to optimize this. Normally I would just do
UPDATE table SET field = field + 1 WHERE id IN (all the ids here)
but now I have the problem that there are id's that occur twice (or more, I can't know that on forehand).
Is there a way to have the query run twice for id 4 if the query looks like this:
UPDATE table SET field = field + 1 WHERE id IN (1, 2, 3, 4, 4, 5)
Thanks,
lordstyx
Edit: sorry for not being clear enough.
The id here is an auto inc field, so it are all unique ID's. the id's that have to be updated are indirectly comming from users, so I can't predict which id is going to occur how often.
If there are the ID's (1, 2, 3, 4, 4, 5) I need the field of row with id 4 to be incremented with 2, and all the rest with 1.
If (1, 2, 3, 4, 4, 5) comes from a SELECT id ... query, then you can do something like this:
UPDATE yourTable
JOIN
( SELECT id
, COUNT(id) AS counter
....
GROUP BY id
) AS data
ON yourTable.id = data.id
SET yourTable.field = yourTable.field + data.counter
;
Since the input comes from users, perhaps you can manipulate it a bit. Change (1, 2, 3, 4, 4, 5) to (1), (2), (3), (4), (4), (5).
Then (having created a temporary table):
CREATE TABLE tempUpdate
( id INT )
;
Do the following procedure:
add the values in the temporary table,
run the update and
delete the values.
Code:
INSERT INTO TempUpdate
VALUES (1), (2), (3), (4), (4), (5)
;
UPDATE yourTable
JOIN
( SELECT id
, COUNT(id) AS counter
FROM TempUpdate
GROUP BY id
) AS data
ON yourTable.id = data.id
SET yourTable.field = yourTable.field + data.counter
;
DELETE FROM TempUpdate
;
No. But you could perform something like
UPDATE table
SET field = field + (LENGTH(',1,2,3,4,4,5,') - LENGTH(REPLACE(',1,2,3,4,4,5,', CONCAT(',', id, ','), ''))) / LENGTH(CONCAT(',', id, ','))
WHERE id IN (1, 2, 3, 4, 4, 5)
if you need row with id = 4 specifically to be incremented twice
Here is solution you wanted, but I'm not sure this is what you need.
Let's say that your talbe is called test. You want to increase id. I've added a field idwas to easily show what was the id before the query:
CREATE TABLE `test` (
`id` int(10) unsigned NOT NULL auto_increment,
`idwas` int(8) unsigned default NULL,
PRIMARY KEY (`id`)
) ;
Let's fill it with data:
truncate table test;
insert into test(id) VALUES(1),(3),(15);
update test set idwas = id;
Now let's say that you have user input 1,3,5,3, so:
id 1 should be increased by 1
id 3 should be increased by 2
id 5 is missing, nothing to increase.
row with id 15 should not be changed because not in user input
We'll put the user input in a variable to be easier to use it:
SET #userInput = '1,3,5,3';
then do the magic:
SET #helperTable = CONCAT(
'SELECT us.id, count(us.id) as i FROM ',
'(SELECT ',REPLACE(#userInput, ',',' AS `id` UNION ALL SELECT '),
') AS us GROUP BY us.id');
SET #stmtText = CONCAT(
' UPDATE ',
'(',#helperTable,') AS h INNER JOIN test as t ON t.id = h.id',
' SET t.id = t.id + h.i');
PREPARE stmt FROM #stmtText;
EXECUTE stmt;
And this is the result:
mysql> SELECT * FROM test;
+----+-------+
| id | idwas |
+----+-------+
| 2 | 1 |
| 5 | 3 |
| 15 | 15 |
+----+-------+
3 rows in set (0.00 sec)
If it's reasonable, you could try doing a combination of what you had before and what you have now.
In whatever is creating this list, separate it into (depending on the language's constructs) some type of array. Follow this by sorting it,finding how many multiples of each there are, and doing whatever else you need to to get the following: an array with (increment-number => list of ids), so you do one query for each increment amount. Thus, your example becomes
UPDATE table SET field = field + 1 WHERE id IN (1, 2, 3, 5)
UPDATE table SET field = field + 2 WHERE id IN (4)
In php, for example, I would take the array, sort the array, use the content of the array as the keys for another array of the form (id => count), and then fold that over into the (count => list of ids) array.
It's not that efficient, but is definitely better than one query per id. It's also probably better than using iteration and string manipulation in SQL. Unless you're forced to use SQL to do everything (which it sounds like you're not), I wouldn't use it to do everything, when it's overly awkward to do so.
You could use the following:
create temporary table temp1 (id integer);
insert into temp1 (id) values (1),(2),(3),(4),(4),(5);
update your_table set your_field = your_field + (select count(*) from temp1 where id = your_table.id)
This solution requires you to format the id list like (1),(2),(3),(4),(4),(5) but I don't think that is a problem, right?
This worked on my test database, hope it works for you too!
Regards,
Arthur

Can you specify an order unmatched rows are inserted in a SQL Server 2008 MERGE Statement?

I am using a MERGE statement to basically do an UPSERT. For the rows that are not present in the destination, I would like them inserted in a certain order. Unfortunately, it seems the ORDER BY clause isn't supported with a merge statement. Is there any way do this in one statement? See example for better idea of what I am trying to do:
CREATE TABLE #destination (ident int not null identity(1,1), id int not null, value int not null)
INSERT INTO #destination (id,value) VALUES (1,50)
CREATE TABLE #source (id int not null, value int not null)
INSERT INTO #source (id,value) VALUES (1,100),(3,300),(2,200)
MERGE #destination d
USING #source s
ON d.id = s.id
WHEN MATCHED THEN
UPDATE
SET d.value = s.value
WHEN NOT MATCHED THEN
INSERT (id,value)
VALUES (s.id,s.value);
SELECT * FROM #destination ORDER BY ident
/*
WILL LIKELY SEE:
1, 1, 100
2, 3, 300
3, 2, 200
WANT TO ACHIEVE:
1, 1, 100
2, 2, 200
3, 3, 300
*/
The reason I want to do this is I would like to write a unit test for my code that performs this merge and want the insertions in a deterministic order. I know there are ways to get around this, but if there is a way to order the insertion of a MERGE it would be the easiest.
So right now this is not possible. From the documentation on MERGE, the operation of inserts, deletes, and updates is unordered; but it was burried in it's explanation of how TOP affects it:
The TOP clause further reduces the
number of joined rows to the specified
value and the insert, update, or
delete actions are applied to the
remaining joined rows in an unordered
fashion. That is, there is no order in
which the rows are distributed among
the actions defined in the WHEN
clauses. For example, specifying TOP
(10) affects 10 rows; of these rows, 7
may be updated and 3 inserted, or 1
may be deleted, 5 updated, and 4
inserted and so on.
Not sure if this would be acceptable in your case, but you could use SET IDENTITY_INSERT to override the identity column and guarantee your order that way.
CREATE TABLE #destination (ident int not null identity(1,1), id int not null, value int not null)
INSERT INTO #destination (id,value) VALUES (1,50)
CREATE TABLE #source (id int not null, value int not null)
INSERT INTO #source (id,value) VALUES (1,100),(3,300),(2,200)
SET IDENTITY_INSERT #destination ON
MERGE #destination d
USING #source s
ON d.id = s.id
WHEN MATCHED THEN
UPDATE
SET d.value = s.value
WHEN NOT MATCHED THEN
INSERT (ident,id,value)
VALUES (s.id, s.id, s.value);
SET IDENTITY_INSERT #destination OFF
SELECT * FROM #destination ORDER BY ident