MySQL update column from another table - mysql

I have two tables, I want to grab the total number of skills required for a specific job position and update this table's column with that information.
The tskill column is the one intended for the information to be updated to.
pnum sname
===============
001 cooking
001 cleaning
002 teaching
003 driving
pnum ptitle tskill
===================
001 chef
002 teacher
003 driving instructor
Sample data with relevant columns (tskill is a new added column in POSITIONS which is currently all empty)
INSERT INTO POSITIONS VALUES ( 001, 'chef')
INSERT INTO POSITIONS VALUES ( 002, 'teacher')
INSERT INTO POSITIONS VALUES ( 003, 'driving instructor')
INSERT INTO SNEEDED VALUES ( 001, 'cooking');
INSERT INTO SNEEDED VALUES ( 001, 'cleaning');
INSERT INTO SNEEDED VALUES ( 002, 'teaching');
INSERT INTO SNEEDED VALUES ( 003, 'driving');
Any tips for doing this with the update statement?

You should be able to update the position table using this query (SQLFiddle):
UPDATE POSITIONS
SET tskill = (SELECT COUNT(pnum)
FROM SNEEDED WHERE SNEEDED.pnum = POSITIONS.pnum)
Output:
select * from POSITIONS;
pnum ptitle tskill
1 chef 2
2 teacher 1
3 driving instructor 1

I have edited this answer as per your question. Have a look again. What we are doing here is , we are selecting the count of pnum from sneeded table with the similar pnum and updating to positions table where pnum matches. Hope this helps.
UPDATE position as p
(
SELECT pnum, count(pnum) as skill FROM sneeeded
GROUP BY pnum
) as n
SET p.tskill = n.skill
where p.pnum = n.pnum

Related

How to update and insert into db with #transactional on db query

I have one table warehouse_item_mapping
having columns - warehouseId, itemId, stockQuantity
warehouse_id item_id stock_quantity
1 123 10
1 234 20
1 345 30
This table is being used as inventory management as maintaining current stock of items in warehouse.
Now if new stock has come with some items including new items with current stock quantity.
Like
1 123 50
1 234 50
1 678 50 (new item)
I have to update the stock quantity in table as sum of existing quantity and current stock quantity. And if mapping not found in table then insert that new mapping.
For this case table should be updated as
warehouse_id item_id stock_quantity
1 123 60
1 234 70
1 345 30
1 678 50
How to do this in single query.
Follow up question in this is
If someone used the current stock from table in between get and update query then how to maintain lock or transactional for whole flow.
INSERT ON DUPLICATE KEY UPDATE is what you need. In combination with pre selected stock_quantity. You need to make item_id PRIMARY KEY.
Now of course you will be using one SELECT and one INSERT INTO statement with PHP ( i guess) variables.
SELECT #stock_quantity:=stock_quantity FROM warehouse_item_mapping WHERE item_id=123;
INSERT INTO warehouse_item_mapping (warehouse_id, item_id, stock_quantity)
VALUES (1, 123, 50)
ON DUPLICATE KEY UPDATE warehouse_id=1, stock_quantity=#stock_quantity+50;
Working example: db-fiddle
RESULT:
Results
Query #1 Execution time: 0ms
warehouse_id item_id stock_quantity
1 123 60
1 234 70
1 345 30
1 678 50
SO lats say you are using PHP:
PHP:
'$item_id' // your new OR old inserting item id.
'$warehouse_id'
'$stock_quantity' // Your NEW stock number.
START TRANSACTION;
SELECT #stock_quantity:=stock_quantity
FROM warehouse_item_mapping WHERE item_id='$item_id';
INSERT INTO warehouse_item_mapping (warehouse_id, item_id, stock_quantity)
VALUES ('$warehouse_id', '$item_id', '$stock_quantity')
ON DUPLICATE KEY UPDATE warehouse_id='$warehouse_id', stock_quantity=#stock_quantity+'$stock_quantity';
COMMIT;
This will try to insert a value only if PRIMARY KEY item-id doesn't exists. If it does it will update other two fields.
EDIT:
I think you are confused what #transaction is, read about it here.
Above is example of one as you asked for it but this can also be done in one query as mentioned in comment. Example:
INSERT INTO warehouse_item_mapping (warehouse_id, item_id, stock_quantity)
VALUES (1, 123, 50) ON DUPLICATE KEY UPDATE
warehouse_id=1, stock_quantity=stock_quantity+50;
Working Fiddle.
PHP:
INSERT INTO warehouse_item_mapping (warehouse_id, item_id, stock_quantity)
VALUES ('$warehouse_id', '$item_id', '$stock_quantity') ON DUPLICATE KEY
UPDATE warehouse_id='$warehouse_id', stock_quantity=stock_quantity+'$stock_quantity';

INSERT id from other column or NULL if value does not exist

THE DATA
I have a set of data in the following format:
CAR_MAKE TABLE
ID MAKE
1 Ford
2 Tesla
3 Acura
4 Honda
5 Toyota
MAKE_NOTES TABLE
NOTE_ID MAKE_ID MAKE_NAME NOTE
1 1 Ford New QNX-Based Sync System
2 2 Tesla Looking forward to Model 3
3 5 Toyota Updated Corolla 2018
4 Null Ferrari Very Fast and Very Red
I know there's repeating data (make_name) between table 1 and table 2. Let's assume I can't mess with the data. I also can't guarantee that an entry would have been made in car_make first. In such a case make_notes.make_id should be null.
WHAT I'VE DONE SO FAR
What I'm trying to do is INSERT a row into make_notes, inserting null into make_id if it does not exist in car_make, otherwise inserting car_make.id.
This works fine if, make_name exists in car_make... but if I attempt to insert a record with a make_name that does not exist in car_make, no record is inserted (no error is thrown either).
INSERT INTO make_notes (
make_id,
make_name,
note
)
SELECT
id,
'ford',
'New Note'
FROM car_make
WHERE make = 'ford';
I've also tried to use this as a subquery:
SELECT
CASE
WHEN (SELECT EXISTS (SELECT 1 FROM car_make where make = 'Ferrari') = 1)
THEN car_make.id
ELSE null
END AS make_id
FROM car_make;
I have not been albe intergrate it into my main query without throwing an error. As a stand alone query it returns one row for each entry in car_make, as null if 'Ferrari' does not exist, and each id if it does.
QUESTION
How do I create an insert query that will insert into 'make_notes', and insert 'null' if make_name does not exist in car_make, and insert car_make.id if make_name does exist?
I think using an IF() clause should work for your case:
INSERT INTO `make_notes` (
make_id,
make_name,
note
)
VALUES(
IF(
((SELECT COUNT(*) FROM `car_make` WHERE `make` = 'ford') > 0),
(SELECT `id` FROM `car_make` WHERE `make` = 'ford'),
NULL
),
'ford',
'New note'
);
I don't think you can do that with one query. I think you have to run a select to get the ID from the car_make table. Then you do the insert.

SQL Query for exact match in many to many relation

I have the following tables(only listing the required attributes)
medicine (id, name),
generic (id, name),
med_gen (med_id references medicine(id),gen_id references generic(id), potency)
Sample Data
medicine
(1, 'Crocin')
(2, 'Stamlo')
(3, 'NT Kuf')
generic
(1, 'Hexachlorodine')
(2, 'Methyl Benzoate')
med_gen
(1, 1, '100mg')
(1, 2, '50ml')
(2, 1, '100mg')
(2, 2, '60ml')
(3, 1, '100mg')
(3, 2, '50ml')
I want all the medicines which are equivalent to a given medicine. Those medicines are equivalent to each other that have same generic as well as same potency. In the above sample data, all the three have same generics, but only 1 and three also have same potency for the corresponding generics. So 1 and 3 are equivalent medicines.
I want to find out equivalent medicines given a medicine id.
NOTE : One medicine may have any number of generics. Medicine table has around 102000 records, generic table around 2200 and potency table around 200000 records. So performance is a key point.
NOTE 2 : The database used in MySQL.
One way to do it in MySQL is to leverage GROUP_CONCAT() function
SELECT g.med_id
FROM
(
SELECT med_id, GROUP_CONCAT(gen_id ORDER BY gen_id) gen_id, GROUP_CONCAT(potency ORDER BY potency) potency
FROM med_gen
WHERE med_id = 1 -- here 1 is med_id for which you're trying to find analogs
) o JOIN
(
SELECT med_id, GROUP_CONCAT(gen_id ORDER BY gen_id) gen_id, GROUP_CONCAT(potency ORDER BY potency) potency
FROM med_gen
WHERE med_id <> 1 -- here 1 is med_id for which you're trying to find analogs
GROUP BY med_id
) g
ON o.gen_id = g.gen_id
AND o.potency = g.potency
Output:
| MED_ID |
|--------|
| 3 |
Here is SQLFiddle demo

Creating a frequency table in Access VBA

I have a table where different participants are given multiple boxes of medicines on multiple days. I am trying to create a frequency table showing how much medicines have been distributed by the number of boxes to the participants.
The result I'm looking for is -
2 boxes = 1 (since only Lynda got a total of 2 boxes), 4 boxes = 2 (since Ryan and Rinky both got a total of 4 boxes after adding up the medicine boxes)
Please let me know what approach would be the best in this case.
Thanks for your help.
-Nams
I think you want:
SELECT t.SumOf, Count(t.[PARTICIPANT ID]) AS CountOf
FROM (SELECT Table1.[PARTICIPANT ID], Sum(Table1.MEDICINE_BOX) AS SumOf
FROM Table1
GROUP BY Table1.[PARTICIPANT ID]) AS t
GROUP BY t.SumOf;
Where table1 is the name of your table.
If your table is like this:
medicine_dispense
participantID date amount_boxes
ABC 8/29/12 1
ABC 8/30/12 2
XYZ 8/29/12 1
XYZ 8/30/12 1
then a query like this:
select
amount_boxes, count(participantID)
from
medicine_dispense
should work
I'll use generic SQL. You can paste SQL into Access queries in SQL view. (You might have to delete the CHECK() constraint.)
create table participant_meds (
participant varchar(10) not null,
distribution_date date not null default current_date,
num_boxes integer not null check (num_boxes > 0),
primary key (participant, distribution_date)
);
insert into participant_meds values ('Ryan', '2012-02-03', 1);
insert into participant_meds values ('Ryan', '2012-06-07', 3);
insert into participant_meds values ('Rinky', '2012-02-28', 4);
insert into participant_meds values ('Lynda', '2012-03-04', 2);
insert into participant_meds values ('Russ', '2012-04-05', 2);
insert into participant_meds values ('Russ', '2012-05-08', 2);
insert into participant_meds values ('Russ', '2012-06-12', 2);
Resulting data, sorted, for copy/paste.
participant distribution_date num_boxes
Lynda 2012-03-04 2
Rinky 2012-02-28 4
Russ 2012-04-05 2
Russ 2012-05-08 2
Russ 2012-06-12 2
Ryan 2012-02-03 1
Ryan 2012-06-07 3
This query gives you the total boxes per participant.
select sum(num_boxes) boxes, participant
from participant_meds
group by participant;
6;"Russ"
2;"Lynda"
4;"Ryan"
4;"Rinky"
Use that query in the FROM clause as if it were a table. (I'd consider storing that query as a view, because I suspect that the total number of boxes per participant might be useful. Also, Access has historically been good at optimizing queries that use views.)
select boxes num_boxes, count(participant) num_participants
from (select sum(num_boxes) boxes, participant
from participant_meds
group by participant) total_boxes
group by num_boxes
order by num_boxes;
num_boxes num_participants
--
2 1
4 2
6 1

MySQL: Joins vs. Bitwise operator, and performance thereof

There are a number of questions about this subject, but mine is more specific to performance concerns.
With regards to an object, I want to track a multitude of 'attributes', each with a multitude of discrete 'values' (each attribute have between 3 and 16 valid 'values'.) For instance, consider tracking military personnel. The attributes/values might be (not real, I totally made these up):
attribute: {values}
languages_spoken: {english, spanish, russian, chinese, …. }
certificates: {infantry, airborne, pilot, tank_driver…..}
approved_equipment: {m4, rocket_launcher, shovel, super_secret_radio_thingy….}
approved_operations: {reconnaissance, logistics, invasion, cooking, ….}
awards_won: {medal_honor, purple_heart, ….}
… and so on.
One one to do this - the way I want to do this - is to have a personnel table and an attributes table:
personnel table => [id, name, rank, address…..]
personnel_attributes table => [personnel_id, attribute_id, value_id]
along with the associated attributes and values tables.
So if pesonnel_id=31415 is approved for logistics, there would be the following entry in the personnel_attributes table:
personnel_id | attribute_id | value_id
31415 | 3 | 2
where 3 = attribute_id for "approved_operations" and 2 = value_id for "logistics" (sorry formatting spaces didn't line up.)
Then a search to find all personnel who speak english OR spanish, AND who is infantry OR airborne, AND can operate a shovel OR super_secret_radio_thingy would be something like:
SELECT t1.personnel_id
FROM personnel_attributes t1, personnel_attributes t2, personnel_attributes t3
WHERE ((t1.attribute_id = 1 and t1.value_id = 1) OR (t1.attribute_id = 1 and t1.value_id = 2))
AND ((t2.attribute_id = 2 and t1.value_id = 1) OR (t2.attribute_id = 2 and t1.value_id = 2))
AND ((t3.attribute_id = 3 and t1.value_id = 3) OR (t3.attribute_id = 3 and t1.value_id = 4))
AND t2.personnel_id = t1.personnel_id
AND t3.personnel_id = t1.personnel_id;
Assuming this isn't a totally stupid way to write the SQL query, the problem is that its very slow (even with seemingly relevant indexes.)
So I'm am toying with using bitwise operators instead, where each attribute is a column in a table and each value is a bit. The same search would be:
SELECT personnel_id FROM personnel_attributes
WHERE language & b'00000011'
AND certificates & b'00000011'
AND approved_operations & b'00001100';
I know this does a full table scan, but in my experiments with 350,000 sample personnel, and 16 attributes each, the first method took 20 seconds whereas the bitwise method took 38 milliseconds!
Am I doing something wrong here? Are these the performance results I should expect?
Thanks!
Using the bitwise operation will require evaluating all of the rows. I believe your problem can be solved with a change to your original SELECT statement and how you're joing your tables:
To make it a little easier to read, I've changed attribute values to words instead of integers so it's less confusing while reading through my example, but obviously you can leave them as integers and it concept would still work:
CREATE TABLE PERSONNEL (
ID INT,
NAME VARCHAR(20)
)
CREATE TABLE PERSONNEL_ATTRIBUTES (
PERSONNEL_ID INT,
ATTRIB_ID INT,
ATTRIB_VALUE VARCHAR(20)
)
INSERT INTO PERSONNEL VALUES (1, 'JIM SMITH')
INSERT INTO PERSONNEL VALUES (2, 'JANE DOE')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (1, 1, 'English')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (1, 1, 'Spanish')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (1, 1, 'Russian')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (1, 3, 'Logistics')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (1, 3, 'Infantry')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (2, 1, 'English')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (2, 3, 'Infantry')
SELECT P.ID, P.NAME, PA1.ATTRIB_VALUE AS DESIRED_LANGUAGE, PA2.ATTRIB_VALUE AS APPROVED_OPERATION
FROM PERSONNEL P
JOIN PERSONNEL_ATTRIBUTES PA1 ON P.ID = PA1.PERSONNEL_ID AND PA1.ATTRIB_ID = 1
JOIN PERSONNEL_ATTRIBUTES PA2 ON P.ID = PA2.PERSONNEL_ID AND PA2.ATTRIB_ID = 3
WHERE PA1.ATTRIB_VALUE = 'Spanish' AND (PA2.ATTRIB_VALUE = 'Infantry' OR PA2.ATTRIB_VALUE = 'Airborne')
Have the same issue of using django-bitfield or a separate table for flags.
Inspired by your experiment, I used a 3.5m record table (innodb) and made count() and retrieve queries for both variants. the result was astonishing: approx 5sec vs. 40sec bitfield wins.