Multiple inner join on a table where rows represent data attributes - mysql

I have the following user table (don't ask me why :) )
| id | cid | attr | text | rdate |
---------------------------------------
| 1 | 1 | name | joe | NULL |
| 2 | 1 | date | NULL | 10.05.2014 |
| 3 | 1 | stat | 2 | NULL |
----------------------------------------
| 4 | 2 | name | joe | NULL |
| 5 | 2 | date | NULL | 05.05.2014 |
| 6 | 2 | stat | 1 | NULL |
----------------------------------------
| 7 | 3 | name | joe | NULL |
| 8 | 3 | date | NULL | 03.05.2014 |
| 9 | 3 | stat | 2 | NULL |
As you can see every user's attribute (name, date, stat) is a row in the table.
Attributes with the same cid belong to the same user.
I would like to delete all the entries which refer to a user whose attribute date is before 08.05.2014 AND whose attribute stat is not 2. So after running this query the table will be:
| id | cid | attr | text | rdate |
---------------------------------------
| 1 | 1 | name | joe | NULL |
| 2 | 1 | date | NULL | 10.05.2014 |
| 3 | 1 | stat | 2 | NULL |
----------------------------------------
| 7 | 3 | name | joe | NULL |
| 8 | 3 | date | joe | 03.05.2014 |
| 9 | 3 | stat | 2 | NULL |
Is it possible? Is this a inner join on the same table?

Group by the cid and use the having clause to run group functions to check out your requirements in every single group
delete from your_table
where cid in
(
select * from
(
select cid
from your_table
group by cid
having sum(attr = 'date' and `date` < '2014-05-08') > 0
and sum(attr = 'stat' and `text` = 2) = 0
) tmp_tbl
)
In MySQL you can't delete from the same table you are selecting from. But you can trick MySQL with another subquery like in the example above.

You can do this with delete/join:
delete t
from table t join
(select cid
from table t
group by cid
having max(case when attr = 'date' and date < '2014-05-08') > 0 and
max(case when attr = 'stat' and text <> '2') > 0
) s
on t.cid = s.cid;

I would do a fairly simple JOIN in the delete statement:-
DELETE a
FROM some_table a
INNER JOIN some_table b
ON a.cid = b.cid
INNER JOIN some_table c
ON a.cid = c.cid
WHERE b.attr = 'date' AND b.date < '2014-05-08'
AND c.attr = 'stat' AND c.text != '2'

Join will do :
Delete from mytable where cid in (select cid from
(select t1.cid FROM mytable t1 INNER JOIN mytable t2 ON t1.cid = t2.cid
WHERE t1.attr = 'date' AND t1.rdate < '2014-05-08'
AND t2.attr = 'stat' AND t2.text != 2) as sq)

Related

How to select max value from rows and join to another table

I am trying to join two tables with respect to the max values for the values column. I would like to produce the expected results as shown below based on the max value while joining
select * from order
-------------------------
| ID | value | Name |
-------------------------
| 1 | 23 | REM |
| 2 | 0 | SER |
| 3 | 13 | MH |
| 4 | 3 | MH |
| 5 | 1 | MP |
-------------------------
select * from product
-------------------------
| ID | value | Name |
-------------------------
| 1 | 2 | ABC |
| 2 | 2 | DEG |
| 3 | 17 | XYZ |
-------------------------
Desired result:
-------------------------
| ID | Value | Name |
-------------------------
| 1 | 23 | REM |
| 2 | 2 | DEG |
| 3 | 17 | XYZ |
| 4 | 3 | MH |
| 5 | 1 | MP |
-------------------------
I have tried something like below but it's not fetching the value (NAME) from other table
SELECT
MAX(IF(a.value >b.value , a.value ,b.value )) AS Value
from order a left join product b on a.ID= b.ID
Please suggest how to get the expected result from these two tables.
Below is for BigQuery Standard SQL
#standardsql
select as value array_agg(struct(id, value, name) order by value desc limit 1)[offset(0)]
from
(
select * from `project.dataset.order`
union all
select * from `project.dataset.product`
)
group by id
with output
You can do this using a full join:
select id,
(case when p.val is null or p.val < o.val then o.val else p.val end),
(case when p.val is null or p.val < o.val then o.name else p.name end)
from product p full join
order o
using (id);
I just find this the simplest way to think about the problem.

MySql Left Join records where is null with handling validUntil Row

How to select all addons which a specific user has not buyed or are no longer valid?
Assuming currentdate is 2017-03-02 17:00:00
Table1 (users):
+-----------+----------+
| id | username |
+-----------+----------+
| 1 | Walter |
| 2 | Hank |
| 3 | John |
+-----------+----------+
Table2 (buyLog):
+-----------+----------+------------+---------------------+
| id | idUsers | idItems | validUntil |
+-----------+----------+------------+---------------------+
| 1 | 1 | 1 | 2016-03-02 14:15:47 |
| 2 | 1 | 1 | 2018-03-02 14:15:47 |
| 3 | 1 | 2 | 2016-03-02 14:15:47 |
| 4 | 2 | 1 | 2018-03-02 14:15:47 |
+-----------+----------+------------+---------------------+
Table3 (addons):
+-----------+----------+
| id | name |
+-----------+----------+
| 1 | Foo |
| 2 | Bar |
| 3 | Lorem |
+-----------+----------+
Expected output for user with id 1 should be:
+-----------+----------+
| id | name |
+-----------+----------+
| 2 | Bar |
| 3 | Lorem |
+-----------+----------+
See SQL Fiddle here: http://sqlfiddle.com/#!9/16356
Where I have the most problems is to handle the validUntil in the leftJoin.
I think I have to group by during the left join to tread only the most recent validUntil record. Maybe using max(validUntil)?
This code will work
http://sqlfiddle.com/#!9/16356/1/0
SELECT
C.ID AS 'ID',
C.NAME AS 'NAME'
FROM
(SELECT
A.id AS 'ID',A.name AS 'NAME',
CASE
WHEN B.YY > '2017-03-02 17:00:00' THEN 0
ELSE 1 END AS 'Tag'
FROM
addons AS A
LEFT JOIN
(SELECT idItems AS 'XX', MAX(validUntil) AS 'YY'
FROM
buyLog
WHERE idUsers = 1 GROUP BY 1) AS B
ON
A.id = B.XX) AS C
WHERE
C.Tag = 1
My sense is that neither your explanation nor your data set and desired result are adequate to the task of explaining the problem. The following query produces the desired result, but perhaps that's just coincidence...
SELECT a.*
FROM addons a
LEFT
JOIN buylog b
ON b.iditems = a.id
AND b.validuntil > NOW()
LEFT
JOIN users u
ON u.id = b.idusers
AND u.id = 1
WHERE b.validuntil IS NULL
AND u.id IS NULL;

MySQL - IF within SELECT

I have the following MySQL query:
SELECT id, name, parent_id AS pID,
(SELECT parent_id
FROM category
WHERE id = pID)
AS grandparent_id,
(SELECT parent_id
FROM category
WHERE id = grandparent_id)
AS greatgrandparent_id,
(SELECT name
FROM category
WHERE id = pID)
AS parent,
(SELECT name
FROM category
WHERE id = grandparent_id)
AS grandparent,
(SELECT name
FROM category
WHERE id = greatgrandparent_id)
AS greatgrandparent
FROM category
WHERE active = '1'
HAVING grandparent IS NOT NULL
ORDER BY grandparent_id, parent_id, sort, id ASC
When the greatgrandparent IS NULL I want it to display the grandparent value instead. Is this possible?
I've tried CASE WHEN but must be getting the syntax wrong somewhere as it keeps throwing an error.
Simple solution: IFNULL(greatgrandparent, grandparent).
You cannot have a having clause without a group by clause.
May be this code helps you.
SELECT id, name, parent_id AS pID,
case when grandparent IS NOT NULL then
(SELECT parent_id FROM category WHERE id = grandparent_id)
else
(SELECT parent_id FROM category WHERE id = pID)
end as _parent_id,
case when grandparent IS NOT NULL then
(SELECT name FROM category WHERE id = grandparent_id)
else
(SELECT name FROM category WHERE id = pID)
end AS _parent_name
FROM category WHERE active = '1'
ORDER BY grandparent_id, parent_id, sort, id ASC
I would rewrite this using left joins with aliases and coalesce.
Given this
/*
CREATE TABLE CATEGORY(ID INT, NAME VARCHAR(20), PARENT_ID INT);
TRUNCATE TABLE CATEGORY;
INSERT INTO CATEGORY VALUES
(1,'AAAA',2),(2,'AAA',3),(3,'AA',4),(4,'A',5),
(10,'BBBB',11),(11,'BBB',12)
;
*/
This code
SELECT C.ID,C.NAME,C.PARENT_ID ,
CP.NAME AS PARENTNAME,CP.PARENT_ID AS PARENT_PARENT_ID,
CG.NAME AS GRANDPARENTNAME,
CG.PARENT_ID AS GRANDPARENT_PARENT_ID,
CGG.NAME AS GREATGRANDPARENTNAME ,
CGG.PARENT_ID AS GREATGRANDPARENT_PARENT_ID
FROM CATEGORY C
LEFT JOIN CATEGORY CP ON CP.ID = C.PARENT_ID
LEFT JOIN CATEGORY CG ON CG.ID = CP.PARENT_ID
LEFT JOIN CATEGORY CGG ON CGG.ID = CG.PARENT_ID
ORDER BY C.ID;
Results in
+------+------+-----------+------------+------------------+-----------------+-----------------------+----------------------+----------------------------+
| ID | NAME | PARENT_ID | PARENTNAME | PARENT_PARENT_ID | GRANDPARENTNAME | GRANDPARENT_PARENT_ID | GREATGRANDPARENTNAME | GREATGRANDPARENT_PARENT_ID |
+------+------+-----------+------------+------------------+-----------------+-----------------------+----------------------+----------------------------+
| 1 | AAAA | 2 | AAA | 3 | AA | 4 | A | 5 |
| 2 | AAA | 3 | AA | 4 | A | 5 | NULL | NULL |
| 3 | AA | 4 | A | 5 | NULL | NULL | NULL | NULL |
| 4 | A | 5 | NULL | NULL | NULL | NULL | NULL | NULL |
| 10 | BBBB | 11 | BBB | 12 | NULL | NULL | NULL | NULL |
| 11 | BBB | 12 | NULL | NULL | NULL | NULL | NULL | NULL |
+------+------+-----------+------------+------------------+-----------------+-----------------------+----------------------+----------------------------+
and this code
SELECT C.ID,C.NAME,C.PARENT_ID ,
CP.NAME AS PARENTNAME,CP.PARENT_ID AS PARENT_PARENT_ID,
COALESCE(CG.NAME,CP.NAME) AS GRANDPARENTNAME,
COALESCE(CG.PARENT_ID,CP.PARENT_ID) AS GRANDPARENT_PARENT_ID,
COALESCE(CGG.NAME,CG.NAME) AS GREATGRANDPARENTNAME ,
COALESCE(CGG.PARENT_ID,CG.PARENT_ID) AS GREATGRANDPARENT_PARENT_ID
FROM CATEGORY C
LEFT JOIN CATEGORY CP ON CP.ID = C.PARENT_ID
LEFT JOIN CATEGORY CG ON CG.ID = CP.PARENT_ID
LEFT JOIN CATEGORY CGG ON CGG.ID = CG.PARENT_ID
ORDER BY C.ID
results in
+------+------+-----------+------------+------------------+-----------------+-----------------------+----------------------+----------------------------+
| ID | NAME | PARENT_ID | PARENTNAME | PARENT_PARENT_ID | GRANDPARENTNAME | GRANDPARENT_PARENT_ID | GREATGRANDPARENTNAME | GREATGRANDPARENT_PARENT_ID |
+------+------+-----------+------------+------------------+-----------------+-----------------------+----------------------+----------------------------+
| 1 | AAAA | 2 | AAA | 3 | AA | 4 | A | 5 |
| 2 | AAA | 3 | AA | 4 | A | 5 | A | 5 |
| 3 | AA | 4 | A | 5 | A | 5 | NULL | NULL |
| 4 | A | 5 | NULL | NULL | NULL | NULL | NULL | NULL |
| 10 | BBBB | 11 | BBB | 12 | BBB | 12 | NULL | NULL |
| 11 | BBB | 12 | NULL | NULL | NULL | NULL | NULL | NULL |
+------+------+-----------+------------+------------------+-----------------+-----------------------+----------------------+----------------------------+

SQL Select only rows with Max Value on a Column FILTERED by Column

This is a followup question to the excellent answer:
SQL Select only rows with Max Value on a Column
SQLFiddle http://sqlfiddle.com/#!2/3077f/2
Table "yourtable":
| id | val | ignore | content |
-------------------------------
| 1 | 10 | 0 | A |
| 1 | 20 | 0 | B |
| 1 | 30 | 1 | C |
| 2 | 40 | 0 | D |
| 2 | 50 | 0 | E |
| 2 | 60 | 1 | F |
When looking for max val per id, following sql is used:
select yt1.*
from yourtable yt1
left outer join yourtable yt2
on (yt1.id = yt2.id and yt1.val < yt2.val )
where yt2.id is null;
So the result will be in this case
| id | val | ignore | content |
-------------------------------
| 1 | 30 | 1 | C |
| 2 | 60 | 1 | F |
The Question is how to filter out by column "ignore" when it's =1 so the result will be
| id | val | ignore | content |
-------------------------------
| 1 | 20 | 0 | B |
| 2 | 50 | 0 | E |
You need to put the condition both in the subquery and the outer query:
select yt1.*
from yourtable yt1 left outer join
yourtable yt2
on yt1.id = yt2.id and yt1.val < yt2.val and yt2.ignore <> 1
where yt2.id is null and yt1.ignore <> 1;
You can simply add another condition and yt1.ignore <> 1, as below:
select yt1.*
from yourtable yt1
left outer join yourtable yt2
on (yt1.id = yt2.id and yt1.val < yt2.val )
where yt2.id is null and yt1.ignore <> 1;

MySQL - How to update atable based on an internal ordering of the table

If I make a selection from my MySQL table like:
SELECT * FROM mytable ORDER BY country_id, category
I get the following table:
+------------+----------+-------+
| country_id | category | order |
+------------+----------+-------+
| 1 | A | 0 |
| 1 | B | 0 |
| 1 | F | 0 |
| 3 | A | 0 |
| 3 | C | 0 |
| 5 | B | 0 |
| 5 | L | 0 |
| 5 | P | 0 |
+------------+----------+-------+
What I would like to do is update the order column so that the value of that column is the order of that row for it's country_id. In other words the final table should look like this:
+------------+----------+-------+
| country_id | category | order |
+------------+----------+-------+
| 1 | A | 1 |
| 1 | B | 2 |
| 1 | F | 3 |
| 3 | A | 1 |
| 3 | C | 2 |
| 5 | B | 1 |
| 5 | L | 2 |
| 5 | P | 3 |
+------------+----------+-------+
If I use the original query given at the top in a subquery, that would give me the correct order but I can't figure out how to iterate through the table and suspect I thinking about it wrongly.
How can I get this result?
Many thanks
Try to use this query -
UPDATE mytable t1
JOIN (
SELECT t1.country_id, t1.category, COUNT(*) `order`
FROM mytable t1
LEFT JOIN mytable t2
ON t2.country_id = t1.country_id AND t2.category <= t1.category
GROUP BY
t1.country_id, t1.category
) t2
ON t1.country_id = t2.country_id AND t1.category = t2.category
SET t1.`order` = t2.`order`
As an aside note, you can remove order field from the table because this can calculated this on the fly.