Leetcode Rearrange Products Table Medium Level - mysql

I saw one answer in discussion as below
SELECT A.product_id, B.COLUMN_NAME as store,
CASE
WHEN B.COLUMN_NAME = "store1" THEN store1
WHEN B.COLUMN_NAME = "store2" THEN store2
WHEN B.COLUMN_NAME = "store3" THEN store3
END AS price
FROM Products A, INFORMATION_SCHEMA.COLUMNS B
WHERE table_name = 'Products' AND column_name != "product_id"
HAVING price IS NOT NULL
Can someone please explain how can we write this INFORMATION_SCHEMA and could still get the output. This is in MYSQL

A couple of techniques are used to pull this off:
Cross Join: Instead of joining the two tables on a key, instead the OOP is joining every record of Products with every records of INFORMATION_SCHEMA.COLUMNS (where the column_name isn't product_id). Essentially they are making an intermediate result set that looks like:
+------------+--------+--------+--------+------------+-------------+
| product_id | store1 | store2 | store3 | table_name | column_name |
+------------+--------+--------+--------+------------+-------------+
| 0 | 95 | 100 | 105 | Products | store3 |
| 0 | 95 | 100 | 105 | Products | store2 |
| 0 | 95 | 100 | 105 | Products | store1 |
| 1 | 70 | | 80 | Products | store3 |
| 1 | 70 | | 80 | Products | store2 |
| 1 | 70 | | 80 | Products | store1 |
+------------+--------+--------+--------+------------+-------------+
You can see each row of products is triplicated as there are three rows of column metadata in INFORMATION_SCHEMA.COLUMNS.
A Case Expression to generate a new column on this intermediate result set that pulls the correct store column's int value based on the INFORMATION_SCHEMA.COLUMNS.column_name for that row:
SELECT A.*, B.COLUMN_NAME as store,
CASE
WHEN B.COLUMN_NAME = "store1" THEN store1
WHEN B.COLUMN_NAME = "store2" THEN store2
WHEN B.COLUMN_NAME = "store3" THEN store3
END AS price
FROM Products A, INFO_SCHEMA B
WHERE table_name = 'Products' AND column_name != "product_id"
HAVING price IS NOT NULL;
+------------+--------+--------+--------+--------+-------+
| product_id | store1 | store2 | store3 | store | price |
+------------+--------+--------+--------+--------+-------+
| 0 | 95 | 100 | 105 | store3 | 105 |
| 0 | 95 | 100 | 105 | store2 | 100 |
| 0 | 95 | 100 | 105 | store1 | 95 |
| 1 | 70 | | 80 | store3 | 80 |
| 1 | 70 | | 80 | store1 | 70 |
+------------+--------+--------+--------+--------+-------+
I included the original columns from Products table to show what the case expression is doing.
The sneaky HAVING clause tossed on at the end. Because the price columns derived by the case expression doesn't exist at the point of query execution when the WHERE clause is executed, OOP snuck in this HAVING clause which evaluates near the end of the sql execution steps when the price column was made into existence. The reason I say it's sneaky is because HAVING without a GROUP BY or any aggregation at all is an oddball and may fail in other RDBMS (but I'd have to test).

Related

How to Join Multiple Queries with Multiple Conditions in Mysql from Single Table in PHP?

I have a single table in MySQL
------------------------------
| ID | Subject | Marks | Term |
-------------------------------
| 1 | English | 45 | 1 |
| 2 | Hindi | 34 | 1 |
| 3 | English | 54 | 2 |
| 4 | Hindi | 33 | 2 |
------------------------------
I want the output like:
--------------------------------------
| Subject | Marks_Term1 | Marks_Term2 |
--------------------------------------
| English | 45 | 54 |
| Hindi | 34 | 33 |
--------------------------------------
Please help me with the code, reply will be appreciated.
SELECT marks_entry.subject_id, marks_entry.main_exam, marks_entry.main_exam
FROM marks_entry
WHERE marks_entry.term_id = 1
LEFT JOIN SELECT marks_entry.subject_id, marks_entry.main_exam, marks_entry.main_exam FROM marks_entry WHERE marks_entry.term_id = 2
Try this,
SELECT A.Subject, A.Marks AS Marks_Term1, B.Marks AS Marks_Term2
FROM {tableName} A, {tableName} B
WHERE A.Subject = B.Subject
You can use conditional aggregation to pivot your dataset:
select
subject,
max(case when term = 1 then marks end) marks_term1,
max(case when term = 2 then marks end) marks_term2
from mytable
group by subject

How to update a column with the number of rows that have a matching column pair?

I have a table called related_clues which lists the id's of pairs of clues which are related
| id | clue_id | related_clue_id | relatedness |
+----+---------+-----------------+-------------+
| 1 | 1 | 232 | 1 |
| 2 | 1 | 306 | 1 |
| 3 | 1 | 458 | 1 |
| 4 | 2 | 620 | 1 |
| 5 | 2 | 72 | 1 |
| 6 | 3 | 212 | 1 |
| 7 | 3 | 232 | 1 |
| 8 | 3 | 412 | 1 |
| 9 | 3 | 300 | 1 |
+----+---------+-----------------+-------------+
Eventually after a while we may reach two id's such as:
+--------+---------+-----------------+-------------+
| id | clue_id | related_clue_id | relatedness |
+--------+---------+-----------------+-------------+
| 121267 | 1636 | 38 | 1 |
| 121331 | 1636 | 38 | 1 |
+--------+---------+-----------------+-------------+
So in this case, for two distinct id values, we have the same (clue_id, related_clue_id) pair
In this case I would like the relatedness value to be updated to 2, signalling that there are two examples of this (clue_id, related_clue_id) pair. Like so:
+--------+---------+-----------------+-------------+
| id | clue_id | related_clue_id | relatedness |
+--------+---------+-----------------+-------------+
| 121267 | 1636 | 38 | 2 |
| 121331 | 1636 | 38 | 2 |
+--------+---------+-----------------+-------------+
So essentially I would like to run some SQL that sets the relatedness value to the number of times a (clue_id, related_clue_id) pair appears.
When I have no relatedness column present, and I simply run the SQL:
SELECT id, clue_id, related_clue_id, COUNT(*) AS relatedness
FROM `related_clues`
GROUP BY clue_id, related_clue_id
It gives me the required result, but of course this doesn't store the relatedness column, it simply shows the column if I run this select. So how do I permanently have this relatedness column?
You could use a update with join
Update related_clues a
INNER JOIN (
SELECT clue_id, related_clue_id, COUNT(*) AS relatedness
FROM `related_clues`
group by clue_id, related_clue_id
having count(*) = 2
) t on t.clue_id = a.clue_id
and t.related_clue_id = a.related_clue_id
set a.relatedness = t.relatedness
I would approach this as an update/join but filter out rows that don't need to be updated:
update related_clues rc join
(select clue_id, related_clue_id, COUNT(*) AS cnt
from `related_clues`
group by clue_id, related_clue_id
) t
on t.clue_id = rc.clue_id and
t.related_clue_id = rc.related_clue_id
set rc.relatedness = t.relatedness
where rc.relatedness <> t.relatedness;

efficient query to group by one field and put all other fields in the row SQL

Imagine the result of a query is something like the following:
+----+---------+--------+
| id | count | type |
+----+---------+--------+
| 1 | 20 | a |
| 1 | 30 | b |
| 1 | 10 | c |
| 2 | 05 | a |
| 2 | 20 | b |
| 2 | 40 | c |
+----+---------+--------+
and the expected result:
+----+---------+--------+------+
| id | a | b | c |
+----+---------+--------+------+
| 1 | 20 | 30 | 10 |
| 2 | 05 | 20 | 40 |
+----+---------+--------+------+
I know some solutions which are complex using Cursor, Variables, Join and etc. I would like to find the most efficient one, otherwise I will handle it from the application layer.
One method uses conditional aggregation:
select id,
sum(case when type = 'a' then count else 0 end) as a,
sum(case when type = 'b' then count else 0 end) as b,
sum(case when type = 'c' then count else 0 end) as c
from t
group by id;

Insert X times based on column

I have a table that has the following properties.
| UPC | Cost | Items |
--------------------------
| abc | 2.50 | 30 |
| 123 | 2.11 | 40 |
Let's say I need to copy the information into another table, but I need to do each one as its own line item... for example, I need to end with...
| UPC | Cost | Sold | ID |
------------------------------
| abc | 2.50 | NULL | 1 |
| abc | 2.50 | NULL | 2 |
...
| abc | 2.50 | NULL | 29 |
| abc | 2.50 | NULL | 30 |
| 123 | 2.11 | NULL | 31 |
| 123 | 2.11 | NULL | 32 |
...
| 123 | 2.11 | NULL | 69 |
| 123 | 2.11 | NULL | 70 |
Is there a way to insert based off # of items in the original table?
I was thinking I could do something like this...
WHILE (SELECT Total FROM dbo.tempInventory) > 0
BEGIN
INSERT INTO dbo.Inventory (UPC, Cost, Sold)
SELECT (UPC, Cost, NULL)
FROM dbo.tempInventory
UPDATE dbo.tempInventory
SET Total = Total-1
END
And this would work for 1 UPC at a time. The issue is I'm working with over 3500 UPC's, and each have between 1 and 60 items to input.
I found a way to do it directly in SQL, but to be honest I'm not 100% sure HOW it works. Would anyone be able to explain?
WITH tally AS (
SELECT 1 n
UNION ALL
SELECT n + 1 FROM tally WHERE n < 100
)
SELECT UPC, n.n Position
FROM dbo.tempInventory t JOIN tally n
ON n.n <= t.Items
ORDER BY Description, Position

Get MAX row for GROUP in MySQL

I have the following data:
+---------+----------+----------+--------+
| id | someId | number | data |
+---------+----------+----------+--------+
| 27 | 123 | 1 | abcde1 |
| 28 | 123 | 3 | abcde2 |
| 29 | 123 | 1 | abcde3 |
| 30 | 123 | 5 | abcde4 |
| 31 | 124 | 4 | abcde1 |
| 32 | 124 | 8 | abcde2 |
| 33 | 124 | 1 | abcde3 |
| 34 | 124 | 2 | abcde4 |
| 35 | 123 | 16 | abcde1 |
| 245 | 123 | 3 | abcde2 |
| 250 | 125 | 0 | abcde3 |
| 251 | 125 | 1 | abcde4 |
| 252 | 125 | 7 | abcde1 |
| 264 | 125 | 0 | abcde2 |
| 294 | 123 | 0 | abcde3 |
| 295 | 126 | 0 | abcde4 |
| 296 | 126 | 0 | abcde1 |
| 376 | 126 | 0 | abcde2 |
+---------+----------+----------+--------+
And I want to get a MySQL query that gets me the data of the row with the highest number for each someId. Note that id is unique, but number isn't
SELECT someid, highest_number, data
FROM test_1
INNER JOIN (SELECT someid sid, max(number) highest_number
FROM test_1
GROUP BY someid) t
ON (someid=sid and number=highest_number)
Unfortunately it is not look quite efficient. In Oracle it could be possible to user OVER clause without subqueries, but MySQL…
Update 1
If there are several instances of highest number this will returs also several data for each pair of someid and number.
To get the only row per each someid we should preaggregate the source table to make someid and number pairs unique (see t1 subquery)
SELECT someid, highest_number, data
FROM
(SELECT someid, number, MIN(data) data
FROM test_1
GROUP BY
someid, number) t1
INNER JOIN
(SELECT someid sid, max(number) highest_number
FROM test_1
GROUP BY someid) t2
ON (someid=sid and number=highest_number)
Update 2
It is possible to simplify previous solution
SELECT someid,highest_nuimber,
(select min(data)
from test_1
where someid=t1.someid and number=highest_nuimber)
FROM
(SELECT someid, max(number) highest_nuimber
FROM test_1
GROUP BY someid) t1
If we materialize unique pairs of someid and number than it is possible to use correlated subquery. Unlike a JOIN it would not produce additional rows if highest value of number is repeated several times.
Slight tweak to Naeel's answer but to return just a single data result for any someId even if there's a tie you should add a GROUP BY:
SELECT t1.someid, t1.number, t1.data
FROM Table1 t1
INNER JOIN (SELECT someId sid, max(number) max_number
FROM Table1
GROUP BY someId) t2
ON (someId = sid AND number = max_number)
GROUP BY t1.someId
SQL Fiddle here