I have 3 tables: A, B and C.
A has AID, B has AID and BID, and C has BID Value and Date.
I need to create a query that returns me AID and the first (according to date) Value from C.
WHAT I've tried:
SELECT A.AID, Value FROM A INNER JOIN B on A.AID = B.BID
INNER JOIN C ON C.BID = B.BID GROUP BY A.AID
It gives me the last Value and not the first.
Data example:
A:
AID:
1
2
3
B:
AID BID
1 1
1 2
2 3
3 4
3 5
3 6
C:
BID Value Date
1 15 1.1.1970
1 422 1.1.1992
2 945 1.1.1975
3 149 1.1.1994
3 147 1.1.2015
4 110 1.1.2004
5 142 1.1.2005
The output should be:
AID Value
1 15
2 149
3 110
If you do not have too many records with the same value and value doesn't have any commas, then the group_concat()/substring_index() trick is probably the easiest way:
select b.aid,
substring_index(group_concat(c.value order by c.date desc), ',' 1) as first_value
from c join
b
on c.bid = b.bid
group by b.aid;
Larger amounts of data require a more complicated query. Something like:
select b.aid, c.value
from c join
b
on c.bid = b.bid
where c.date = (select min(c2.date)
from c2 join
b2
on c2.bid = b2.bid
where b2.aid = b.aid
);
To restrict C to just those rows with the latest (minimum) date you need a subquery that will produce the minimum date, then use that to limit the rows from C
SELECT
A.AID
, C.Value
FROM A
INNER JOIN B ON A.AID = B.BID
INNER JOIN C ON b.bid = c.bid
INNER JOIN (
SELECT
bid
, MIN(date) AS mindate
FROM c
GROUP BY
bid
) AS m ON c.bid = m.bid
AND c.date = m.mindate
DROP TABLE IF EXISTS b;
CREATE TABLE b
(aid INT NOT NULL
,bid INT NOT NULL
,PRIMARY KEY(aid,bid)
);
INSERT INTO b VALUES
(1 ,1),
(1 ,2),
(2 ,3),
(3 ,4),
(3 ,5),
(3 ,6);
DROP TABLE IF EXISTS c;
CREATE TABLE c
(bid INT NOT NULL
,value INT NOT NULL
,date DATE
,PRIMARY KEY(bid,date)
);
INSERT INTO c VALUES
(1 ,15 ,'1970-01-01'),
(1 ,422 ,'1992-01-01'),
(2 ,945 ,'1975-01-01'),
(3 ,149 ,'1994-01-01'),
(3 ,147 ,'2015-01-01'),
(4 ,110 ,'2004-01-01'),
(5 ,142 ,'2005-01-01');
SELECT x.aid
, y.value
FROM b x
JOIN c y
ON y.bid = x.bid
JOIN
( SELECT b.aid
, MIN(c.date) min_date
FROM b
JOIN c
ON c.bid = b.bid
GROUP
BY b.aid
) z
ON z.min_date = y.date
AND z.aid = x.aid;
+-----+-------+
| aid | value |
+-----+-------+
| 1 | 15 |
| 2 | 149 |
| 3 | 110 |
+-----+-------+
Related
If I have a table:
id1 id2 count
A A 1
A B 2
A C 1
B A 3
B B 1
B C 2
C A 3
C B 2
C C 1
What I want after deleting:
id1 id2 count
A A 1
A B 2
A C 1
B B 1
B C 2
C C 1
which means if I have A(id1) --> B(id2) then delete B(id1) --> A(id2). same as B(id1) --> C(id2) then delete the row C(id1) --> B(id2)
Thank you for ur help!
In this case we analyze Target.id1 > Target.id2 mean case like (B, A, ??) where B > A
this also ignore cases like (A, A, ??)
Then use self left join to try find another row with (A, B, ??)
If we found a match then Source.id1 IS NOT NULL and we delete
SQL Fiddle Demo
DELETE Target
FROM Table1 Target
LEFT JOIN Table1 Source
ON Target.`id1` = Source.`id2`
AND Target.`id2` = Source.`id1`
AND Target.`id1` > Target.`id2`
WHERE Source.`id1` IS NOT NULL;
OUTPUT
| id1 | id2 | count |
|-----|-----|-------|
| A | A | 1 |
| A | B | 2 |
| A | C | 1 |
| B | B | 1 |
| B | C | 2 |
| C | C | 1 |
Should be something like:
DELETE FROM 'myTable'
WHERE STRCMP(id1, id2) > 0;
STRCMP function can compare the strings and return an int. From there it should be easy - something very similar to the above. If you have further trouble let me know.
It looks like what you are saying is...
If there is a (id1,id2) tuple in the table with values e.g. (a,b), and there is another tuple (b,a) that consists of the the same values, but swapped in the columns, you want to remove one of those tuples. It looks like the one you want to remove is the one that has the "greater" value in the first column.
First, identify the "duplicate" tuples.
For now, we'll ignore the tuples where the values of id1 and id2 are the same, e.g. (a,a).
SELECT s.id1
, s.id2
FROM mytable s
WHERE s.id1 > s.id2
AND EXISTS ( SELECT 1
FROM mytable r
WHERE r.id1 = s.id2
AND r.id2 = s.id1
)
ORDER BY s.id1, s.id2
If that returns the set of rows you want to remove, we can convert that into a DELETE. To do that, we need to change that query into an inline view,
We can re-write that to be like this, verify we get equivalent results.
SELECT o.id1, o.id2
FROM ( SELECT q.id1, q.id2
FROM ( SELECT s.id1, s.id2
FROM mytable s
WHERE s.id1 > s.id2
AND EXISTS ( SELECT 1
FROM mytable r
WHERE r.id1 = s.id2
AND r.id2 = s.id1
)
) q
GROUP BY q.id1, q.id2
) p
JOIN mytable o
ON o.id1 = p.id1
AND o.id2 = p.id2
ORDER BY o.id1, o.id2
Then we can convert that to a DELETE statement, replacing SELECT o.id1, o.id2 WITH DELETE o.* and removing the ORDER BY...
DELETE o.*
FROM ( SELECT q.id1, q.id2
FROM ( SELECT s.id1, s.id2
FROM mytable s
WHERE s.id1 > s.id2
AND EXISTS ( SELECT 1
FROM mytable r
WHERE r.id1 = s.id2
AND r.id2 = s.id1
)
) q
GROUP BY q.id1, q.id2
) p
JOIN mytable o
ON o.id1 = p.id1
AND o.id2 = p.id2
I have two tables:
Table A
ID | DATE | VALUE | KEY|
1 30.8.14 100 11
2 25.8.14 500 11
2 20.8.14 250 11
Table B
ID | DATE | VALUE | KEY|
1 30.8.14 AB 11
2 25.8.14 CD 11
3 10.8.14 EF 11
These two tables should be merged, key is used to define which entries should be merged WHERE KEY = '11'
IF there is a date in TABLE A that is also in TABLE B, it becomes on entry with both values
IF there is no date in TABLE A that is also in TABLE B, the value for B becomes (null)
And in the End, there should be only 1 date field.
Columns should also be be a unique name..
I created this example table, how my output should look like
joinedDate | aValue | bValue
30.8.14 100 AB
25.8.14 500 CD
20.8.14 250 (null)
10.8.14 (null) EF
Im using MySQL version 5.5 on Maria DB
Could someone help me here?
You seem to want full outer join, which MySQL doesn't offer. Here is one method:
select d.date, a.value as avalue, b.value as bvalue
from ((select date from a union
select date from b
)
) d left join
a
on a.date = d.date left join
b
on b.date = d.date;
select a.date, a.value as avalue, b.value as bvalue
from tablea a
left join tableb b
on a.date = b.date
union all
select b.date, null, b.value
from tableb b
left join tablea a
on a.date = b.date
where a.date is null
Fiddle:
http://sqlfiddle.com/#!2/09ab9e8/4/0
I have a transaction table with a schema like
id | order_id | response | amount
1 | 2 |'payment' | 1000
2 | 5 |'declined'| 0
3 | 5 |'declined'| 0
4 | 5 |'payment' | 500
5 | 5 |'declined'| 0
6 | 11 |'declined'| 0
7 | 11 |'declined'| 0
9 | 11 |'declined'| 0
What I wand to do is find all orders, where the three most recent transactions for that order are 'declined'. Assume the higher the id, the more recent the transaction (or you can assume there is a created_at column).
In the above case, the only order_id that should be returned is 11 because while order_id 5 has 3 declined transactions, the most recent 3 transaction are D P D
Is there a clean way to do this with pure sql that runs in a reasonable about of time (assuming ~50M rows).
Assuming the higher id the more recent:
SELECT t0.order_id
FROM transaction t0
JOIN transaction t1 ON
((t1.response=t0.response) AND (t1.order_id=t0.order_id) AND
t1.id=(SELECT MAX(id) FROM transaction WHERE id<t0.id and t0.order_id=order_id))
JOIN transaction t2 ON
((t2.response=t0.response) AND (t2.order_id=t0.order_id) AND
t2.id=(SELECT MAX(id) FROM transaction WHERE id<t1.id AND t0.order_id=order_id))
WHERE t0.response='declined' AND
t0.id=(SELECT MAX(id) FROM transaction WHERE order_id=t0.order_id);
Not a fast solution at all, but it should give you what you want (I assume recent transaction is transaction with higher value of id column) :
SELECT * FROM
(
SELECT *,
(
SELECT COUNT(1)
FROM `transaction` a WHERE a.order_id = b.order_id AND
a.id >= b.id
)as num
FROM `transaction`b
) a WHERE num =3
AND NOT EXISTS
(
SELECT NULL FROM `transaction` b where response<>'declined'
and b.order_id = a.order_id and b.id >=a.id
)
Here's one way...
SELECT DISTINCT a.order_id
FROM
( SELECT x.*
, COUNT(*) rank
FROM my_table x
JOIN my_table y
ON y.order_id = x.order_id
AND y.id >= x.id
GROUP
BY id HAVING COUNT(*) <= 3
) a
LEFT
JOIN
( SELECT x.*
, COUNT(*) rank
FROM my_table x
JOIN my_table y
ON y.order_id = x.order_id
AND y.id >= x.id
GROUP
BY id HAVING COUNT(*) <= 3
) b
ON b.order_id = a.order_id
AND b.response <> 'declined'
WHERE b.id IS NULL;
Fiddle of same: http://www.sqlfiddle.com/#!2/386aa3/1
Just a thought... does this work (untested)...
SELECT DISTINCT x.order_id
FROM my_table x
JOIN my_table y
ON y.order_id = x.order_id
AND y.id >= x.id
GROUP
BY x.id
HAVING COUNT(*) = 3
AND COUNT(*) = SUM(CASE WHEN y.response = 'declined' THEN 1 ELSE 0 END);
The following is an example structure:
table `pligg`
#id #alpha #num
1 a null
2 b null
3 c null
4 a null
5 d null
6 b null
7 a null
8 e null
I'd like to update the databse like this after a single mysqli query:
table `pligg`
#id #alpha #num
1 a 1
2 b 1
3 c 1
4 a 2
5 d 1
6 b 2
7 a 3
8 e 1
What I'm trying to do is to update the column num with the number of duplicate.
I tried this query, but in vain
UPDATE pligg SET 'num' = COUNT(DISTINCT alpha) WHERE 'id'<id
This should do the trick:
UPDATE pligg a
INNER JOIN
(
SELECT a.id, a.alpha, COUNT(1) AS dup_cnt
FROM pligg a
INNER JOIN pligg b ON a.id >= b.id AND a.alpha = b.alpha
GROUP BY a.id, a.alpha
) b ON a.id = b.id
SET a.num = b.dup_cnt
SQLFiddle Demo
I have a table call production
factory_id | factory_name | product_id
1 | A | 1
1 | A | 2
1 | A | 3
2 | B | 3
3 | C | 1
3 | C | 2
3 | C | 3
3 | C | 4
3 | C | 5
I'm trying to develop a query that will return two factory name pair such that every product of factory1 is produced by factory2, result looked like:
factory_name_1 | factory_name_2
A | C
B | A
B | C
I have some nested self join and renames, but I can't wrap my head around how I can apply EXISTS or IN for this scenario that does "for each product produced by factory X do condition". Thanks to any help in advanced.
Update:
Sorry that I forgot to paste my query:
select t0.fname0, t1.fname1
from (
select factory_id as fid0, factory_name as fname0, product_id as pid0, count(distinct factory_id, product_id) as pnum0
from production
group by factory_id
) t0
join
(
select factory_id as fid1, factory_name as fname1, product_id as pid1, count(distinct factory_id, product_id) as pnum1
from production
group by factory_id
) t1
where t0.fid0 <> t1.fid1
and t0.pnum0 < t1.pnum1
and t0.pid0 = t1.pid1;
Update 2: production is the only table. Expected output factory1 and factory2 are just the rename of factory_name attribute.
You need to JOIN the table for each factory pairing to make sure they "join" on the same product_ids, otherwise you might end up with similar counts for DISTINCT product_ids but these will not necessarily refer to the same product_ids.
This is my take on it:
SELECT bfna,afna, pcnt FROM (
SELECT a.factory_name afna, b.factory_name bfna, COUNT(DISTINCT b.product_id) commoncnt
FROM tbl a LEFT JOIN tbl b ON b.factory_name!=a.factory_name AND b.product_id=a.product_id
GROUP BY a.factory_name, b.factory_name
) c
INNER JOIN (
SELECT factory_name fna, COUNT(DISTINCT product_id) pcnt
FROM TBL GROUP BY factory_name
) d ON fna=bfna AND commoncnt=pcnt
ORDER BY bfna,afna
You can find a demo here: https://rextester.com/JJGCK84904
It produces:
bfna afna commoncnt
A C 3
B A 1
B C 1
For simplicity I left out the column factory_id as it does not add any information here.
Fun fact: as I am using only "bare-bone" SQL expressions, the above code will run on SQL-Server too without any changes.
You can do it this way:
select A as factory_name_1 , B as factory_name_2
from
(
select A, B, count(*) as Count_
from
(
select a.factory_name as A, b.factory_name as B
from yourtable a
inner join yourtable b
on a.product_id = b.product_id and a.factory_id <> b.factory_id
)a group by A, B
)a
inner join
(select factory_name, count(*) as Count_ from yourtable group by factory_name) b
on a.A = b.factory_name and a.Count_ = b.Count_
Order by 1
Output:
factory_name_1 factory_name_2
A C
B A
B C
The other solutions just seem more complicated than necessary. This is basically a self-join with aggregation:
with t as (
select t.*, count(*) over (partition by factory_id) as cnt
from tbl t
)
select t1.factory_id, t2.factory_id, t1.factory_name, t2.factory_name, count(*)
from t t1 join
t t2
on t1.product_id = t2.product_id and t1.factory_id <> t2.factory_id
group by t1.factory_id, t2.factory_id, t1.factory_name, t2.factory_name, t1.cnt
having count(*) = max(t1.cnt);
Here is a db<>fiddle.