MySQL update multiple rows based on id - mysql

In a table in MySQL I'm trying to update a specific field based on the id.
The table has more than 5,000 rows and many fields. One of the fields is "id" and another one that I want to update is called "category" which right now all of them are NULL and I want to update all of them.
my backup mysql file that I want to use has only "id" and "category" which they are like this:
INSERT INTO `products` (`id`, `category`) VALUES
(3, 1),
(4, 1),
(5, 2),
(6, 1),
(7, 5),
(8, 1),
(9, 6),
(10, 1),
...
(5000, 3);
I want to update the "category" field in my table according to the id's in this list and because there is more than 5,000 rows I don't want to change each record manually.
Right now in my table all the "category" fields are NULL and I want to update or give new information to the "category" fields using the file that I have.

The easiest is to use a Temporary table :
CREATE TEMPORARY TABLE temp_products (id int, category int ) ;
Then
INSERT INTO `temp_products` (`id`, `category`) VALUES
(3, 1),
(4, 1),
(5, 2),
(6, 1),
(7, 5),
(8, 1),
(9, 6),
(10, 1),
...
(5000, 3);
Now you just have to use an update with an inner join :
Update products p
INNER JOIN temp_products t_p ON t_p.id = p.id
SET p.category = t_p.category
if you want you can add a where clause :
Update products p
INNER JOIN temp_products t_p ON t_p.id = p.id
SET p.category = t_p.category
WHERE p.category IS NULL

Perhaps the better solution for you can be:
First of all, create temporary table
CREATE TABLE `products_tmp` (
`id` INT NOT NULL,
`category` INT NOT NULL,
PRIMARY KEY (`id`)
);
After that, perform inserts into generated temporary table:
INSERT INTO `products_tmp` (`id`, `category`) VALUES
(3, 1),
(4, 1),
(5, 2),
(6, 1),
(7, 5),
(8, 1),
(9, 6),
(10, 1),
...
(5000, 3);
after this, you can update all fields in your original table:
UPDATE products p
JOIN products_tmp pt ON p.id = pt.id
SET p.category = pt.category;
After this, you can delete temporary table
drop table products_tmp;

Related

Check if a pair of records belong to multiple group IDs

I have a table that contains 2 IDs - UserID and GroupID. I need to pull a list of all UserIDs that "share" the same GroupID at least 4 times
So, based on the following data set:
CREATE TABLE IF NOT EXISTS `tableA` (
`UserID` int(11) unsigned NOT NULL,
`GroupID` int(11) unsigned NOT NULL
) DEFAULT CHARSET=utf8;
INSERT INTO `tableA` (`UserID`, `GroupID`) VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(1, 2),
(2, 2),
(3, 2),
(1, 3),
(2, 3),
(3, 3),
(1, 4),
(2, 4),
(3, 4),
(1, 5),
(3, 5);
I'm trying to generate the following result:
UserID A
UserID B
NumberOfOccurrences
1
2
4
2
3
4
1
3
5
I've created an SQLFiddle for it. I've tried to achieve this via JOINs and sub-queries, but I'm not entirely sure how to properly proceed with something like this.
Do a self join. GROUP BY. Use HAVING to make sure at least 4 common GroupID's.
select a1.userid, a2.userid
from tablea a1
join tablea a2
on a1.GroupID = a2.GroupID and a1.userid < a2.userid
group by a1.userid, a2.userid
having count(*) >= 4

Select rows grouped by a column having max aggregate

Given the following data set, how would I find the email addresses that were references for the most ApplicationIDs that have an "Accepted" decision?
CREATE TABLE IF NOT EXISTS `EmailReferences` (
`ApplicationID` INT NOT NULL,
`Email` VARCHAR(45) NOT NULL,
PRIMARY KEY (`ApplicationID`, `Email`)
);
INSERT INTO EmailReferences (ApplicationID, Email)
VALUES
(1, 'ref10#test.org'), (1, 'ref11#test.org'), (1, 'ref12#test.org'),
(2, 'ref20#test.org'), (2, 'ref21#test.org'), (2, 'ref22#test.org'),
(3, 'ref11#test.org'), (3, 'ref31#test.org'), (3, 'ref32#test.org'),
(4, 'ref40#test.org'), (4, 'ref41#test.org'), (4, 'ref42#test.org'),
(5, 'ref50#test.org'), (5, 'ref51#test.org'), (5, 'ref52#test.org'),
(6, 'ref60#test.org'), (6, 'ref11#test.org'), (6, 'ref62#test.org'),
(7, 'ref70#test.org'), (7, 'ref71#test.org'), (7, 'ref72#test.org'),
(8, 'ref10#test.org'), (8, 'ref81#test.org'), (8, 'ref82#test.org')
;
CREATE TABLE IF NOT EXISTS `FinalDecision` (
`ApplicationID` INT NOT NULL,
`Decision` ENUM('Accepted', 'Denied') NOT NULL,
PRIMARY KEY (`ApplicationID`)
);
INSERT INTO FinalDecision (ApplicationID, Decision)
VALUES
(1, 'Accepted'), (2, 'Denied'),
(3, 'Accepted'), (4, 'Denied'),
(5, 'Denied'), (6, 'Denied'),
(7, 'Denied'), (8, 'Accepted')
;
Fiddle of same:http://sqlfiddle.com/#!9/03bcf2/1
Initially, I was using LIMIT 1 and ORDER BY CountDecision DESC, like so:
SELECT er.email, COUNT(fd.Decision) AS CountDecision
FROM EmailReferences AS er
JOIN FinalDecision AS fd ON er.ApplicationID = fd.ApplicationID
WHERE fd.Decision = 'Accepted'
GROUP BY er.email
ORDER BY CountDecision DESC
LIMIT 1
;
However, it occurred to me that I could have multiple email addresses that referred different "most accepted" decisions (i.e., a tie, so to speak), and those would be filtered out (is that the right phrasing?) with the LIMIT keyword.
I then tried a variation on the above query, replacing the ORDER BY and LIMIT lines with:
HAVING MAX(CountDecision)
But I realized that that's only half a statement: MAX(CountDecision) needs to be compared to something. I just don't know what.
Any pointers would be much appreciated. Thanks!
Note: this is for a homework assignment.
Update: To be clear, I'm trying to find value and count of Emails from EmailReferences. However, I only want rows that have FinalDecision.Decision = 'Accepted' (on matching ApplicantIDs). Based on my data, the result should be:
Email | CountDecision
---------------+--------------
ref10#test.org | 2
ref11#test.org | 2
For example...
SELECT a.*
FROM
( SELECT x.email
, COUNT(*) total
FROM emailreferences x
JOIN finaldecision y
ON y.applicationid = x.applicationid
WHERE y.decision = 'accepted'
GROUP
BY x.email
) a
JOIN
( SELECT COUNT(*) total
FROM emailreferences x
JOIN finaldecision y
ON y.applicationid = x.applicationid
WHERE y.decision = 'accepted'
GROUP
BY x.email
ORDER
BY total DESC
LIMIT 1
) b
ON b.total = a.total;
MySQL still lack window functions, but when version 8 is production ready, this becomes easier. So for fuure reference, or for those databases like Mariadb that already have window functions:
CREATE TABLE IF NOT EXISTS `EmailReferences` (
`ApplicationID` INT NOT NULL,
`Email` VARCHAR(45) NOT NULL,
PRIMARY KEY (`ApplicationID`, `Email`)
);
INSERT INTO EmailReferences (ApplicationID, Email)
VALUES
(1, 'ref10#test.org'), (1, 'ref11#test.org'), (1, 'ref12#test.org'),
(2, 'ref20#test.org'), (2, 'ref21#test.org'), (2, 'ref22#test.org'),
(3, 'ref30#test.org'), (3, 'ref31#test.org'), (3, 'ref32#test.org'),
(4, 'ref40#test.org'), (4, 'ref41#test.org'), (4, 'ref42#test.org'),
(5, 'ref50#test.org'), (5, 'ref51#test.org'), (5, 'ref52#test.org'),
(6, 'ref60#test.org'), (6, 'ref11#test.org'), (6, 'ref62#test.org'),
(7, 'ref70#test.org'), (7, 'ref71#test.org'), (7, 'ref72#test.org'),
(8, 'ref10#test.org'), (8, 'ref81#test.org'), (8, 'ref82#test.org')
;
CREATE TABLE IF NOT EXISTS `FinalDecision` (
`ApplicationID` INT NOT NULL,
`Decision` ENUM('Accepted', 'Denied') NOT NULL,
PRIMARY KEY (`ApplicationID`)
);
INSERT INTO FinalDecision (ApplicationID, Decision)
VALUES
(1, 'Accepted'), (2, 'Denied'),
(3, 'Accepted'), (4, 'Denied'),
(5, 'Denied'), (6, 'Denied'),
(7, 'Denied'), (8, 'Accepted')
;
select email, CountDecision
from (
SELECT er.email, COUNT(fd.Decision) AS CountDecision
, max(COUNT(fd.Decision)) over() maxCountDecision
FROM EmailReferences AS er
JOIN FinalDecision AS fd ON er.ApplicationID = fd.ApplicationID
WHERE fd.Decision = 'Accepted'
GROUP BY er.email
) d
where CountDecision = maxCountDecision
email | CountDecision
:------------- | ------------:
ref10#test.org | 2
dbfiddle here

Selecting only the first two items from and order

I need you help regarding something, i have 3 tables ORDERS, ORDER_ITEM, ORDER_ITEM_LINE.
CREATE TABLE orders
(`id` int, `date` datetime)
;
INSERT INTO orders
(`id`, `date`)
VALUES
(78, '2017-01-03 00:00:00'),
(79, '2017-02-03 00:00:00'),
(80, '2017-03-03 00:00:00'),
(81, '2017-04-03 00:00:00'),
(82, '2017-05-03 00:00:00'),
(83, '2017-06-03 00:00:00'),
(84, '2017-07-03 00:00:00')
;
CREATE TABLE order_item
(`id` int, `fk_o_id` int, `sku` int)
;
INSERT INTO order_item
(`id`, `fk_o_id`, `sku`)
VALUES
(10, 78, 123),
(11, 79, 124),
(12, 79, 125),
(13, 80, 126),
(14, 82, 127),
(15, 82, 128),
(16, 82, 129)
;
CREATE TABLE order_item_line
(`id` int, `fk_oi_id` int, `line_id` int)
;
INSERT INTO order_item_line
(`id`, `fk_oi_id`, `line_id`)
VALUES
(33, 10, 1),
(34, 11, 1),
(35, 12, 2),
(36, 13, 1),
(37, 14, 1),
(38, 15, 2),
(39, 16, 3)
;
I would like to display all orders with 2 or more than 2 items but only first two so it will be line_id - 1 and 2.
The outcome should look like:
Outcome
If you have any ideas, thank you in advance.
To get the result you require, you will need to create another table. In this example I created a table called TESTQUERY and inserted data to count how many times the orders id appeared
Table creation
CREATE TABLE TESTQUERY
(`id` int, `count` int)
Data into the test table
INSERT INTO TESTQUERY
(
SELECT o.id, COUNT(o.id) as count FROM orders o
JOIN order_item oi ON oi.fk_o_id = o.id
JOIN order_item_line oil ON oil.fk_oi_id = oi.id
GROUP BY o.id
)
I then queried against all for databases using the query below and it returned your desired outcome
SELECT o.id, oi.sku, oil.line_id FROM orders o
JOIN order_item oi ON oi.fk_o_id = o.id
JOIN order_item_line oil ON oil.fk_oi_id = oi.id
JOIN TESTQUERY t ON t.id = o.id
WHERE t.count > 1 AND oil.line_id < 3
I hope this helps

MySQL: Select first row with value in interval

With the following table:
CREATE TABLE table1 (`id` INT, `num` INT);
INSERT INTO table1 (`id`, `num`) VALUES
(1, 1),
(1, 5),
(1, 7),
(1, 12),
(1, 22),
(1, 23),
(1, 24),
(2, 1),
(2, 6);
How do I select a row for each num interval of 5 (ie. select the first row for [0,5), the first for [5,10), the first for [10,15), etc.), with a given id? Is this possible with a MySQL query, or must I process it in a programming language later?
For reference, the output I'd want for id=1:
(1, 1), (1,5), (1,12), (1,22)
Here is a short query:
select min(num), ceiling((num + 1)/5)
from table1
where id = 1
group by ceiling((num + 1)/5);

SELECT data based on result of previous row in table

I have a database of students.
CREATE TABLE classlist
(`id` int, `studentid` int, `subjectid` int, `presentid` int)
;
CREATE TABLE student
(`id` int, `name` varchar(4))
;
CREATE TABLE subject
(`id` int, `name` varchar(4))
;
CREATE TABLE classStatus
(`id` int, `name` varchar(8))
;
INSERT INTO classlist
(`id`, `studentid`, `subjectid`, `presentid`)
VALUES
(1, 111, 1, 1),
(2, 222, 3, 0),
(3, 333, 2, 1),
(4, 111, 4, 1),
(5, 111, 1, 0),
(6, 222, 3, 0),
(7, 333, 2, 1),
(8, 111, 4, 1),
(9, 111, 2, 0),
(10, 111, 4, 1),
(11, 111, 1, 1),
(12, 333, 3, 1),
(13, 333, 2, 1),
(14, 333, 3, 1)
;
INSERT INTO student
(`id`, `name`)
VALUES
(111, 'John'),
(222, 'Kate'),
(333, 'Matt')
;
INSERT INTO subject
(`id`, `name`)
VALUES
(1, 'MATH'),
(2, 'ENG'),
(3, 'SCI'),
(4, 'GEO')
;
INSERT INTO classStatus
(`id`, `name`)
VALUES
(0, 'Absent'),
(1, 'Present')
;
And I have a query which shows how many times they have been present or absent.
SELECT
studentid,
students.name AS NAME,
SUM(presentid = 1) AS present,
SUM(presentid = 0) AS absent
FROM classlist
INNER JOIN student as students ON classlist.studentid=students.id
GROUP BY studentid, NAME
See this fiddle below.
http://sqlfiddle.com/#!2/fe0b0/1
There seems to be a trend from looking at this sample data that after someone attends subjectid 4 they are often not coming to the next class. How can I capture this in a query. I want to ONLY show data WHERE last subjectid =4. So in my sample data rows matching my criteria would be.
(5, 111, 1, 0),
(9, 111, 2, 0),
(11, 111, 1, 1),
as these rows are all the next row of a studentid who had a subjectid=4.
My output would be
| STUDENTID | NAME | PRESENT | ABSENT|
| 111 | John | 1 | 2 |
To get the next class for a student, use a correlated subquery:
select cl.*,
(select min(cl2.id) from classlist cl2 where cl2.studentid = cl.studentid and cl2.id > cl.id) as nextcl
from classlist cl
Plugging this into your query example tell you you who is present and absent for the next class:
SELECT students.id, students.name AS NAME,
SUM(cl.presentid = 1) AS present, SUM(cl.presentid = 0) AS absent,
sum(clnext.presentid = 1) as presentnext
FROM (select cl.*,
(select min(cl2.id) from classlist cl2 where cl2.studentid = cl.studentid and cl2.id > cl.id) as nextcl
from classlist cl
) cl INNER JOIN
student as students
ON cl.studentid = students.id left outer join
classlist clnext
on cl.nextcl = clnext.id
GROUP BY students.id, students.NAME
Add a where cl.subjectid = 4 to get the answer for subject 4.
I fixed the query. The SQLFiddle is k.
A quick and dirty solution could be to get the Classlist.Id for all lines where subjectid=4 (let's call them n) then select all the lines where Id = n+1