MySQL - Combine SELECT and UPDATE? - mysql

I would like MySQL to compare the values of 2 columns in a table. If both values are the same, an UPDATE-statement must be executed on another table. These are my 2 tables:
TABLE: EMPLOYEES
EMPLOYEE_ID | NAME | CREDIT
---------------------------
1 | John | 5
2 | Bill | 10
3 | Mark | 7
TABLE: BONUSES
BONUS_ID | EMPLOYEE_ID | A | B | AMOUNT
---------------------------------------
1 | 1 | x | x | 6
2 | 2 | x | y | 19
3 | 2 | y | x | 4
4 | 3 | y | y | 12
5 | 3 | x | x | 15
If in the bonuses-table the value of column A is equal to the value of column B, the "amount" value of that row must be added to the employees credit in the employees-table. In SQL, it would be something like this:
SELECT * FROM bonuses WHERE A = B;
..and after that:
UPDATE employees SET credit = credit + bonuses.amount
-> For example:
In the "bonuses" table, the first row value A is euqal to value B. That means that the employee with EMPLOYEE_ID 1 (John) must have added 6 to their credit. The same goes for the 4th row where value A is equal to value B. In that case, the employee with EMPLOYEE_ID 3 (Mark) must have added 12 to their credit.
Does anyone know how to do this? Thanks!

You can use a multiple-table update to join the tables:
UPDATE EMPLOYEES JOIN BONUSES USING (EMPLOYEE_ID)
SET EMPLOYEES.CREDIT = EMPLOYEES.CREDIT + BONUSES.AMOUNT
WHERE BONUSES.A = BONUSES.B
However, you may wish to consider whether this logic would be better implemented in a trigger:
CREATE TRIGGER foo AFTER INSERT ON BONUSES FOR EACH ROW
IF NEW.A = NEW.B THEN
UPDATE EMPLOYEES
SET CREDIT = CREDIT + NEW.AMOUNT
WHERE EMPLOYEE_ID = NEW.EMPLOYEE_ID
END IF
;

Try this query
update EMPLOYEE E
inner join BONUSES B on
E.EMPLOYEE_ID = B.EMPLOYEE_ID AND B.A = B.B
set E.CREDIT = E.CREDIT + B.AMOUNT;

Related

SQL select only items that happen after but not also before a certain date

I have a table in SQL that contains People's IDs, codes and entry dates for each code.
Table X:
PERSON_ID CODE ENTRY_DATE
1 A 2017-12-03
1 C 2016-01-13
1 C 2009-05-11
2 B 2007-03-25
2 F 2018-01-18
3 G 2003-04-09
And another table that contains the person_id and reference dates for each person.
Table Y:
PERSON_ID REF_DATE
1 2015-07-18
2 2017-06-17
3 2002-10-06
What I want to do is for each person select rows from table X for which codes happened after REF_DATE in TABLE Y but the CODE itself didn't also occur before REF_DATE. For Example, in the case of person 1, the codes that happened after 2015-07-18 are A (2017-12-03) and the first C (2016-01-13). But Since C also occurred before REF_DATE (2015-07-18) in 2009-05-11, C is not to be selected.
This is just an example, the actual tables have millions of rows and thousands of different codes so I can't manually type codes etc.
the expected result of the query in this example should be:
PERSON_ID CODE ENTRY_DATE
1 A 2017-12-03
2 F 2018-01-18
3 G 2003-04-09
Any idea how to code that in SQL ?
Thanks !
First you join both tables so you have REF_DATE then filter the rows to get only the ones after REF_DATE, but also make sure doesnt exist any row before that date with that code.
SQL DEMO
SELECT X.`PERSON_ID`, X.`CODE`, X.`ENTRY_DATE`, Y.`REF_DATE`
FROM TableX X
JOIN TableY Y
ON X.`PERSON_ID` = Y.`PERSON_ID`
WHERE X.`ENTRY_DATE` > Y.`REF_DATE`
AND NOT EXISTS (SELECT 1
FROM TableX
WHERE TableX.`PERSON_ID` = X.`PERSON_ID`
AND TableX.`CODE`= X.`CODE`
AND TableX.`ENTRY_DATE` < Y.`REF_DATE`
)
OUTPUT
| PERSON_ID | CODE | ENTRY_DATE | REF_DATE |
|-----------|------|----------------------|----------------------|
| 1 | A | 2017-12-03T00:00:00Z | 2015-07-18T00:00:00Z |
| 2 | F | 2018-01-18T00:00:00Z | 2017-06-17T00:00:00Z |
| 3 | G | 2003-04-09T00:00:00Z | 2002-10-06T00:00:00Z |

Modify MySQL Query or Run in PHP

I'm trying to run a query to find which inventory I should promote and which campaign I should run so I can move that inventory.
I have three tables:
campaigns lists different campaigns that I can run, each campaign has a unique id. Some campaigns promote only one item and some promote multiple items.
inventory has all the items I have in stock and the quantity of those items.
campaign_to_inventory matches the unique campaign id to the inventory item.
campaigns:
name | id
-------------|---
blue-widgets | 1
gluten-free | 2
gadget | 3
inventory:
item | qty
-------|----
thing1 | 0
thing2 | 325
thing3 | 452
thing5 | 123
thing7 | 5
campaign_to_inventory:
id | item
---|-------
1 | thing1
1 | thing2
1 | thing5
2 | thing1
2 | thing3
3 | thing7
I'd like to run a query to find all the campaigns I could run where I have the needed inventory in stock. I'm currently running this query:
SELECT * FROM `campaigns` LEFT JOIN `campaign_to_inventory` ON `campaigns`.`id` = `campaign_to_inventory`.`id` LEFT JOIN `inventory` ON `campaign_to_inventory`.`item` = `inventory`.`item`
Which returns:
name | id | item | qty
-------------|----|--------|----
blue-widgets | 1 | thing1 | 0
blue-widgets | 1 | thing2 | 325
blue-widgets | 1 | thing5 | 123
gluten-free | 2 | thing1 | 0
gluten-free | 2 | thing3 | 452
gadget | 3 | thing7 | 5
Should I use PHP to process this data to find only campaigns where all item quantities are greater than a minimum threshold, or is there a way to modify the query to limit the rows there? Is there a rule of thumb of when I can/should do it in one and not the other?
There's no need to process the data in PHP.
One way to do this would be to select the campaign_to_inventory.id column where the number of items is less than your threshold, like this:
SET #min_qty = 1;
SELECT `c_to_i`.`id` FROM `campaign_to_inventory` AS `c_to_i`
INNER JOIN `inventory` ON `inventory`.`item` = `c_to_i`.`item`
WHERE `inventory`.`qty` <= #min_qty;
... And then do a left outer join from campaign_to_inventory to that like this:
SET #min_qty = 1;
SELECT `id`, `name` FROM `campaigns`
LEFT JOIN (
/* Table of campaigns which contain items with not enough qty*/
SELECT `c_to_i`.`id` FROM `campaign_to_inventory` AS `c_to_i`
INNER JOIN `inventory` ON `inventory`.`item` = `c_to_i`.`item`
WHERE `inventory`.`qty` <= #min_qty
) AS `campaigns_with_not_enough_items`
ON `campaigns`.`id` = `campaigns_with_not_enough_items`.`id`
WHERE `campaigns_with_not_enough_items`.`id` is NULL;
The result should be a table of campaigns which have the needed inventory in stock.
As an aside, you should rename your campaign_to_inventory.id column to campaign since the name id implies that the column is the primary key for the table.

Update without where clause

+------+------+
| id | no |
+------+------+
| 1 | 1 |
| 11 | 1 |
| 21 | 1 |
+------+------+
I want to update 2nd row no. to 2.
the query i can use is
update test set no = 2 where id = 11;
How can i achieve the same without where clause ??
I am not sure why you would want to but...
UPDATE `test` SET `no` = IF(`id`=11, 1, `no`);
For the record, I would be surprised if this didn't perform horribly as it would go through every row in the table.
To update the "second" row in the table, the row that has the second smallest id value...
UPDATE test t
JOIN ( SELECT r.id
FROM test r
ORDER BY r.id
LIMIT 1,1
) s
ON s.id = t.id
SET t.no = 2
EDIT
As a followup to clarify the results of the query above...
In the case where id is not unique in the table, the query could potentially update more than one row. The inline view query (s) gets the id value from the "second" row, after the rows are ordered by id value. Then all rows that have that same id value would be updated.
This is an issue only if id is not unique; if id is unique, the statement would update (at most) one row.
For example, if the contents of the table was:
+-----+-----+
| id | no |
+-----+-----+
| 1 | 1 |
| 11 | 3 | <-- "second" row, ordered by id ascending
| 11 | 4 | <-- id from third row matches id from second row
| 21 | 1 |
+-----+-----+
The result of the query above would be to update the two rows that have id value of 11.
+-----+-----+
| id | no |
+-----+-----+
| 1 | 1 |
| 11 | 2 | <-- updated
| 11 | 2 | <-- updated
| 21 | 1 |
+-----+-----+
That´s not possible, a update without where is an update to all the table. You can try this, but it is always like a where:
update test set no = case id when 11 then 2 else no end
This doesn't use a where clause and it might be a bit faster than using if() or case:
update test t join
(select 1 as dum) dum
on t.id = 11
set t.no = 2 ;
And yet a 3rd way...
update test A INNER JOIN test B
on A.ID = B.ID
and B.ID = 11
set A.No = 2;
For clarity this does a self join on a table that only has record 11, thus updating only record 11 (b.iD = 11). using an ON Clause.

Update Statement Using Max Date and userid as the criteria

So I am trying to Update a contract table where the Contract Start Date is the latest date and the relevant employee id. The Contract Table stores all past information about the employee.
eg.
contract_tbl
+------------+------------+--------------------+-----------------+---------------+
|Contractid |EmployeeId |ContractStartDate |ContractEndDate | Position |
+------------+------------+--------------------+-----------------+---------------+
| 1 | 1 | 2012-12-13 | 2013-12-12 | Data Entry |
+------------+------------+--------------------+-----------------+---------------+
| 2 | 1 | 2014-01-26 | 2015-01-25 | Data Entry |
+------------+------------+--------------------+-----------------+---------------+
| 3 | 2 | 2014-01-26 | 2015-01-25 | Data Entry |
+------------+------------+--------------------+-----------------+---------------+
This is the SQL that I have but it does not work. (using a mysql db)
UPDATE contract_tbl
SET Position='Data Analyst'
WHERE EmployeeId = 1 And ContractStartDate= (
select max(ContractStartDate
FROM contract_tbl))
So it should Update the second row shown above with Data Analyst in the Position column but I am getting an error.
Does anybody have any idea how to fix this?
Thanks in advance
This will also do:
UPDATE contract_tbl a
JOIN (
SELECT MAX(ContractStartDate) m
FROM contract_tbl
WHERE EmployeeId = 1) b ON a.ContractStartDate = b.m AND a.EmployeeId = 1
SET a.Position='Data Analyst';
Probably this is what you want:
UPDATE contract_tbl c1
SET Position='Data Analyst'
WHERE EmployeeId = 1 And ContractStartDate= (
SELECT max(ContractStartDate)
FROM contract_tbl c2
WHERE c2.EmployeeId = c1.EmployeeId
)

Finding cooccuring values in MYSQL weak relation table

I have a weak relation table, called header, it is basically just three ID's: id is an autoincrement primary key, did points to the id of table D and hid points to the id of table H. D and H are irrelevant here.
I want to find for any value of hid, the other values of hid that shares did with the original hid. An example:
id | did | hid
===============
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 4
6 | 2 | 5
7 | 3 | 2
8 | 3 | 6
For hid = 1 I would thus like to find id = {2,3,5,6} as those are the rows that have did in common with hid = 1.
I can do this by creating some arrays in PHP and running through all possible values of hid and respective did, but this is a quite slow process for large tables. I was wondering if there is a clever kind of JOIN or similar statement that could be used to find the cooccuring values of hid.
If I have understood you correctly:-
SELECT a.hid, GROUP_CONCAT(b.id)
FROM header a
INNER JOIN header b
ON a.did = b.did
AND b.hid != 1
WHERE a.hid = 1
GROUP BY a.hid
SQL fiddle:-
http://www.sqlfiddle.com/#!2/9aa26/1
Maybe this:
SELECT d.id
FROM (
SELECT *
FROM header
WHERE header.hid =1
) AS h
JOIN header AS d ON d.did = h.did
WHERE d.hid !=1