I have two tables, A and B, A contains a list of entries and B for each of those entries multiple status rows (0-n, grouped by date with a status 0 for okay and 1 for failure).
Now I would like to select all rows from A with their respective most recent status and its date as well as the most recent failure and its date (failure defined as having at least one entry with 1).
I tried something with two left joins but am not convinced it is the optimal solution and also still have issues with determining the correct number of failures (SUM(b2.status))
SELECT a.id, b1.date, SUM(b1.status), b2.date, SUM(b2.status) FROM tablea a
LEFT JOIN tableb b1 ON b1.aid=a.id
LEFT JOIN tableb b2 ON b2.aid=a.id
WHERE (b1.date=(SELECT MAX(`date`) FROM tableb WHERE aid=a.id) OR b1.date IS NULL)
AND (b2.date=(SELECT MAX(`date`) FROM tableb WHERE aid=a.id GROUP BY `date` HAVING SUM(`status`)>0) OR b2.date IS NULL)
GROUP BY a.id
Whoops, I think every assumption I made to this point has been wrong. I think this will give you what you want. It will give you the total number of failures for the latest date that had a failure.
I'm not sure how performance will be on a very large database, but it works fine on a smaller one.
SELECT a.id, MAX(b1.date) status_date, SUM(b1.status) latest_status,
b2.date latest_failure,
b2.total_failures
FROM tablea a
LEFT JOIN tableb b1
ON b1.aid = a.id
AND b1.date = (SELECT MAX(date) FROM tableb WHERE aid = a.id)
LEFT JOIN
(SELECT aid, date, count(*) total_failures FROM tableb WHERE status > 0 GROUP BY aid, date) b2
ON b2.aid = a.id
AND b2.date = (SELECT MAX(date) FROM tableb WHERE aid = a.id AND status > 0)
GROUP BY a.id;
Something like this should work.
SELECT a.id, b1.date status_date, b1.status, b2.date latest_failure,
(SELECT COUNT(*) FROM tableb WHERE aid = a.id AND status > 0) total_failures
FROM tablea a
LEFT JOIN tableb b1
ON b1.aid = a.id
AND b1.date = (SELECT MAX(date) FROM tableb WHERE aid = a.id)
LEFT JOIN tableb b2
ON b2.aid = a.id
AND b2.date = (SELECT MAX(date) FROM tableb WHERE aid = a.id AND status > 0)
This is assuming that the tableb.date field is a datetime and is unique. Otherwise you may wish to use an id field instead.
You could do this also. Not sure which one is more efficient. You could try it both ways.
SELECT a.id, b1.date status_date, b1.status,
(SELECT MAX(date) FROM tableb WHERE aid = a.id AND status > 0) latest_failure,
(SELECT COUNT(*) FROM tableb WHERE aid = a.id AND status > 0) total_failures
FROM tablea a
LEFT JOIN tableb b1
ON b1.aid = a.id
AND b1.date = (SELECT MAX(date) FROM tableb WHERE aid = a.id)
This should work.
SELECT a.id, MAX(b1.date) status_date, SUM(b1.status) latest_status,
(SELECT MAX(date) FROM tableb WHERE aid = a.id AND status > 0) latest_failure,
(SELECT COUNT(*) FROM tableb WHERE aid = a.id AND status > 0) total_failures
FROM tablea a
LEFT JOIN tableb b1
ON b1.aid = a.id
AND b1.date = (SELECT MAX(date) FROM tableb WHERE aid = a.id)
GROUP BY a.id;
If you could have multiple failures for a day, and want to show the status as "1", then change this:
SUM(b1.status) latest_status
to:
MAX(b1.status) latest_status.
Related
How could I count an inner join output, thanks a lot
-- Quantity A = 981
SELECT COUNT(DISTINCT ID) FROM A;
-- Quantity B = 673
SELECT COUNT(DISTINCT ID) FROM B;
How can i count an inner join
SELECT * FROM A
INNER JOIN B
ON A.ID = B.ID;
Combine your two attempts into one since you're performing an INNER JOIN, it does not matter if you use A.ID or B.ID in the DISTINCT COUNT:
SELECT COUNT(DISTINCT A.ID) AS AB_Count FROM A INNER JOIN B ON A.ID = B.ID;
Fiddle for reference.
I have this query:
select a.*, b.*, (select c.* from tableC c where c.id_tableA = a.id) from tableA a inner join tableB b on a.id = b.id_tableA where b.id_user = 50;
The subquery (which is tableC) is returning me more than 1 row as expected. How can I return only 1 row from tableC so it could match with the rest of the query?
So far I have tried this:
(select c.* from tableC c where c.id_tableA = a.id limit 1)
It didn't work as mysql said:
"Operand should contain 1 column(s)"
You are mixing two things. Scalar subquery in SELECT list should return only one value (both row and column). Using LIMIT 1 will get you one row, but still many columns.
So you could specify column name:
select a.*, b.*,
(select c.col_name from tableC c where c.id_tableA = a.id order by .. limit 1)
from tableA a
inner join tableB b on a.id = b.id_tableA
where b.id_user = 50;
or use normal JOIN:
select a.*, b.*, c.*
from tableA a
inner join tableB b
on a.id = b.id_tableA
left join tableC c
on c.id_tableA = a.id
where b.id_user = 50;
if column id from table C is a primary key then it should have no problem
but if no, try to add another condition that will filter your subquery results like ,
for example here is the start_date:
SELECT a.column_1, b.column_2,
(SELECT column_3 FROM tableC
WHERE (id = a.id
AND (start_date = (SELECT MAX(b.start_date)
from tableC as c
where a.id = c.id ))) AS column_3
FROM tableA as a INNER JOIN
tableB as b ON b.id = a.id
WHERE b.id_user = 50;
I have 2 tables in my SQL database. Let's call them Table A and Table B.
I have joined both the tables using left join on ID = AID. Table A has a field by name ID and Table B has field AID and Price.
The problem is Table B can have multiple prices for the ID present in Table A.
The requirement is - If only one price is present in Table B corresponding to ID in table A then it should return that price.
If more than one price is present in table B for an ID in table A then I should get the price as Zero/null.
Query -
SELECT DISTINCT A.ID,B.Price
from A
left join B
on A.ID = B.AID
where "some condition"
Use Count() with Over() window function to find the count of records for each ID then based on count return the price
Try this way
SELECT DISTINCT A.ID,
case when count(1) over(partition by A.ID) > 1 then NULL else B.Price end
from A
left join B
on A.ID = B.AID
For Mysql
SELECT DISTINCT a.id,
CASE
WHEN c.aid IS NULL THEN NULL
ELSE b.price
END
FROM a
LEFT JOIN b
ON a.id = b.aid
LEFT JOIN (SELECT aid
FROM b
GROUP BY aid
HAVING Count(1) = 1) c
ON a.id = c.aid
For SQL Server (should work for MySQL too). One sub-query (you can put in CTE) that shows only AIDs that have only one price:
SELECT A.ID,
B.Price
FROM A
LEFT JOIN (
SELECT AID
FROM B
GROUP BY AID
HAVING COUNT(Price) = 1
) as C
ON C.AID = A.ID
LEFT JOIN B
ON C.AID = B.AID
Use below query. It should work.
SELECT A.ID, o.Price
FROM A
OUTER APPLY
(
SELECT IIF(COUNT(B.Price)>1,NULL,MAX(B.Price)) AS Price
FROM B
WHERE B.AID = A.ID
) AS o;
I have 2 columns a and b with a 1:n relation:
A.id, a.text, a.b_id(fk), a.value --
B.id(pk), b.text etc
I want to create a query that returns the number of b.id with count(a.value=1) > 0
I tried this:
Select count(b.id)
from a.id
join b on a.b_id=b_id
group by b.id
having count(if(a.value=1),1,null))>0
...but without result. It seems simple but for me is a problem.
You don't need HAVING because standard INNER join won't return b rows without matching a rows anyway. You don't need GROUP BY either - use COUNT(DISTINCT ) instead:
SELECT COUNT(DISTINCT b.id) AS cnt
FROM b
JOIN a ON a.b_id = b.id
WHERE a.value = 1
This should do:
SELECT COUNT(*)
FROM TableB AS B
WHERE EXISTS(SELECT 1 FROM TableA
WHERE id = B.id
AND value = 1)
Try this:-
Select count(b.id) as x
FROM b
JOIN a ON a.b_id = b.id
WHERE a.value = 1
group by b.id
A simplification on #MarcinJuraszek's answer. If the foreign key can be trusted, there is no need to join table b:
SELECT COUNT(DISTINCT a.b_id) AS cnt
FROM a
WHERE a.value = 1 ;
So I have two tables like this:
create table A
{
id int;
...
}
create table B
{
id int;
a_id int;
t timestamp;
...
}
A is one-to-many with B
I want to:
SELECT * FROM A LEFT JOIN B ON A.id = B.a_id ???
But I want to return exactly one row for each entry in A which has the B with the newest t field (or null for Bs fields if it has no B entry).
That is rather than returning all A-B pairs, I want to only select the newest one with respect to A (or A-null if no B entry).
Is there some way to express this in SQL? (I'm using MySQL 5.5)
LEFT JOIN is only concerned with ensuring every row in A is returned, even if there is no corresponding joined row in B.
The need for just one row needs another condition. MySQL is limitted in its options, but one could be:
SELECT
*
FROM
A
LEFT JOIN
B
ON B.id = A.id
AND B.t = (SELECT MAX(lookup.t) FROM B AS lookup WHERE lookup.id = A.id)
Another could be...
SELECT
*
FROM
A
LEFT JOIN
(
SELECT id, MAX(t) AS t FROM B GROUP BY id
)
AS lookup
ON lookup.id = A.id
LEFT JOIN
B
ON B.id = lookup.id
AND B.t = lookup.t
You could do the following:
SELECT A.*, B.*
FROM
A
LEFT JOIN
(SELECT B.a_id, MAX(t) as t FROM B GROUP BY B.a_id) BMax
ON A.id = BMax.a_id
JOIN B
ON B.a_id = BMax.a_id AND B.t = BMax.t
you first need to get the newest t from tableB in a subquery, then join it with tableA and tableB.
SELECT a.*, c.*
FROM tableA a
LEFT JOIN
(
SELECT a_ID, max(t) maxT
FROM tableB
GROUP BY a_ID
) b on a.a_id = b.a_ID
LEFT JOIN tableB c
ON b.a_ID = c.a_ID AND
b.maxT = c.t
try this:
SELECT *
FROM tableA A LEFT JOIN
(select a_id ,max(t) as max_t
from tableB
group by a_id )b
on A.id = b.a_id
and A.t=b.max_t