my query is giving me the wrong ID - sql

I've got four tables: A, B, C, D. D has a FK to C, C has a FK to B, B has a FK to A. Each table has an ID column. In addition to ID and CID, table D has Value and Timestamp (an integer). Given a certain time, I need the row from D before the Timestamp but closest to it for each CID -- Unless there are no rows before the Timestamp, in which case I need the first value after after the Timestamp. I'm using Sqlite.
Leaving out this last requirement, I attempted to get the values before the Timestamp with this query, but the ID returned is wrong:
SELECT *
FROM D d
INNER JOIN C c ON d.CID = c.ID
INNER JOIN B b ON C.BID = b.ID
WHERE b.AID = #AID AND d.Timestamp =
(
SELECT MAX(d2.Timestamp)
FROM D d2
WHERE d2.Timestamp < #StartTime
AND d2.CID = D.CID
)

This query should get the last entry before #StartTime in the first part of the query (before the UNION ALL) then combine it with the first entry after #StartTime. All that remains is to pick the smallest entry for each CID.
SELECT d2.*
FROM (
SELECT D.ID, MAX(D.Timestamp)
FROM D d
INNER JOIN C c ON c.ID = d.CID
INNER JOIN B b on b.ID = c.BID
WHERE b.AID = #AID AND d.Timestamp <= #StartTime
GROUP BY d.CID) AS results
INNER JOIN D d2 ON d2.ID = results.ID
UNION ALL
SELECT d2.*
FROM (
SELECT D.ID, MIN(D.Timestamp)
FROM D d
INNER JOIN C c ON c.ID = d.CID
INNER JOIN B b on b.ID = c.BID
WHERE b.AID = #AID AND d.Timestamp > #StartTime
GROUP BY d.CID) AS results
INNER JOIN D d2 ON d2.ID = results.ID

This might work, but still a little confused over exactly what you want.
SELECT *,
(CASE WHEN d.Timestamp < #StartTime THEN 1 ELSE 0) AS timeCheck
FROM D d
INNER JOIN C c ON d.CID = c.ID
INNER JOIN B b ON C.BID = b.ID
WHERE b.AID = #AID
ORDER BY timeCheck DESC, d.Timestamp DESC
LIMIT 0,1
Updated

Related

How to replace Union in Sql

I need to concatinate results from 2 tables without using UNION.
Ex : I have 4 tables a, b, c, d. see below snips:
Table a:
Table b:
Table c:
Table d:
I am concatinating a and d results using UNION ALL like below:
select a.id,a.seq,a.item,b.des,c.qty from a
left join b on a.item = b.item
left join c on a.id = c.id and a.seq = c.seq
UNION ALL
select d.id,d.seq,d.item,d.des,c.qty from d
join c on d.id = c.id and d.seq = c.seq
My output:
But I need same result without using UNION ALL.
Is it possible if so HOW?
You can apply Full Outer join instead of Union which has its similarities to Union
SELECT a.id,a.seq,a.item,b.des,c.qty
FROM a left join b on a.item = b.item
left join c on a.id = c.id and a.seq = c.seq
FULL OUTER JOIN
(
SELECT d.id,d.seq,d.item,d.des,c.qty
FROM d join c on d.id = c.id and d.seq = c.seq
)x ON a.id = x.id
You can replace union all with full join with a "false" condition and lots of COALESCE()s.
The logic looks like this:
SELECT COALESCE(abc.id, cd.id) as id,
COALESCE(abc.seq, cd.seq) as seq,
COALESCE(abc.item, cd.item) as item,
COALESCE(abc.des, cd.des) as eesc,
COALESCE(abc.qty, cd.qty) as qty
FROM (SELECT a.id, a.seq, a.item, b.des, c.qty
FROM a LEFT JOIN
b
ON a.item = b.item LEFT JOIN
c
ON a.id = c.id AND a.seq = c.seq
) abc FULL JOIN
(SELECT d.id, d.seq, d.item, d.des, c.qty
FROM d JOIN
c
ON d.id = c.id AND d.seq = c.seq
) dc
ON 1 = 0; -- never evaluates to true
There is a LEFT JOIN from table 'a' to table 'c' (on 'id' and 'seq' columns) in the upper query and INNER JOIN from table 'd' to table 'c' in the lower query. Therefore I think you could LEFT JOIN from table 'c' to table 'd' and it would produce the correct output.
select a.id,a.seq,a.item,b.des,c.qty
from a
left join b on a.item = b.item
left join c on a.id = c.id and a.seq = c.seq
left join d on c.id = d.id and c.seq = d.seq;
If you want you can use INSERT INTO a (tmp) table and get the same result in a table.

ORA-01427: single-row subquery returns more than one row error | But I want multiple values

I've below query, into which I'm creating using a subquery which is returning
ORA-01427: single-row subquery returns more than one-row error.
But I want all the values that subquery is returning, and there is no other column left for the join condition. Below is my sample query.
select name,
dob,
cdate,
(select value
from item a,
books b
where a.id = b.id
and a.newid = b.newid
and a.id = s.id
and a.bid = s.cid
and a.eventid=1) col_value,
(select value2
from item a,
books b
where a.id = b.id
and a.newid = b.newid
and a.id = s.id
and a.bid = s.cid
and a.eventid=1) col_value2
from sample s,
purchase p
where s.id = p.id
and s.cid = p.cid
Desired Output
Do I need to apply a Group By? Please let me know your suggestions.
It is little bit more difficult without data but try:
select name -- please use table alias so you know which table the value is from (s, p or cv)
, dob
, cdate
, cv.value
from sample s
left join purchase p on s.id=p.id and s.cid=p.cid --or just join
left join (select value, a.id, a.bid --or just join
from item a
left join books b --or just join
on a.id=b.id and a.newid=b.newid) cv
on cv.id = s.id and cv.bid = s.cid
left join (select value2
from item a
left join books b
on a.id = b.id and a.newid = b.newid) cv2
on cv2.id = s.id and cv2.bid = s.cid
where cv2.eventid=1;
select name,
dob,
cdate,
(select value
from
books b
where a.id = b.id
and a.newid = b.newid ) col_value,
(select value2
from
books b
where a.id = b.id
and a.newid = b.newid ) col_value2
from sample s,
purchase p,item a
where s.id = p.id
and s.cid = p.cid
and a.id(+)=s.id
and a.bid(+)=s.cid
and a.eventid(+)=1

Need an efficient query in the following case

Have 3 tables
Table A
id | value
-----------
|
Table B
id|value|A_id(fk to A)
--------------
| |
Table C
id|value|B_id(FK to B)|timestamp
--------------------------------
| | |
I have written a query to find out all latest distinct C values using the following query
select A.id, B.id, C.timestamp, C.value
from A,B,C
where A.id = B.A_id
and B.id = C.B_id
where C.value in (select distinct value from C c2 where c2.value = c.value and c2.value is not null)
and c.timestamp = (select max(timestamp) from C c3 where c3.value = c.value);
except IDs none of the other columns are having indexes. Right now this query takes about 2 hrs or more to run, because the number of distinct C values are 221000 records. Is there an efficient way to do this?
SELECT distinct A.id, B.id, c.timestamp, c.value FROM
(
SELECT c.value, MAX(c.timestamp) AS max_timestamp FROM c
WHERE NOT c.value IS NULL
GROUP BY c.value) c1 INNER JOIN c ON c1.value = c.value AND c1.max_timestamp = c.timestamp
inner join b ON B.id = C.B_id
inner join a ON A.id = B.A_id
A sub-query inside a query will be run for each row inside the main query.
When having large data inside the main query, that will be a performance anti-pattern (you have 2 sub-queries).
You need a group maximum, that could be achieved with a self left join.
SELECT A.id a_id, B.id b_id, C1.timestamp, C1.value
From C C1
INNER JOIN B on B.id = C1.b_id
INNER JOIN A on A.id = B.A_id
LEFT JOIN C C2 on C1.value = C2.value
and
C1.timstamp < C2.timestamp
WHERE C1.value IS NOT NULL
and C2.id IS NULL
Try one of these. i think i understood your connections. hope it helps. 2nd query might be faster since its trying to gather max timestamp by itself first then joining to other tables ids
select A.id, B.id.C.timestamp,c.value
from A
inner join B on B.B_id = A.id
inner join C on C.C_id = B._B_id
where C.value in (select distinct value from C c2 where c2.value = c.value and c2.value is not null)
and c.timestamp = (select max(timestamp) from C c3 where c3.value = c.value);
WITH
A_ID AS (
SELECT A.id, B.id
from A
inner join B on B.A_id = A.id
)
, C_ID AS (
SELECT C_ID, value, max(timestamp)
from C
where value in (select distinct value from C where value is not null)
)
SELECT a.id, a.b_id, c.B_id, c.timestamp, c.value
FROM A_ID a
inner join C_ID c on c.B_ID = a.B_ID
order by a.id

how to eliminate duplicate results from joined sql statement

i have four tables which are related like this:
TABLE A: 1 to many with TABLE B
TABLE B: 1 to many with TABLE C
TABLE D: many to 1 with TABLE B
I would like to create a result set which contains no duplicates.
SELECT A.f1
A.f2
B.f1
C.f1
D.f1
from A LEFT JOIN (B INNER JOIN D on D.fk_b = B.id) on A.id = B.fk_a
LEFT JOIN C on C.fk_b = B.id
Some but not all records are duplicated. Only the records from TABLE D which have the same FK to TABLE B.
If there are 2 the same D.fk_B fields then i would like to select only the first one. My INNER JOIN would become something like:
B INNER JOIN TOP 1 D on.....
But that's not possible?
Thank you!
Assuming that the structure in question is near the one shown bellow,
The following SELECT statement will show you just the primary keys, briefly giving an idea about what data is being returned.
SELECT
A.id AS `A_id`,
B.id AS `B_id`,
COALESCE(C.id, 0) AS `C_id`,
COALESCE(
(SELECT D.id FROM D WHERE D.fk_b = B.id LIMIT 1), 0
) AS `D_id`
FROM B
INNER JOIN A ON (B.fk_a = A.id)
LEFT JOIN C ON (B.id = C.fk_b);
Now the select you've asked for:
SELECT
A.f1 AS `A_f1`,
A.f2 AS `A_f2`,
B.f1 AS `B_f1`,
COALESCE(C.f1, '-') AS `C_f1`,
COALESCE(
(SELECT D.f1 FROM D WHERE D.fk_b = B.id LIMIT 1), '-'
) AS `D_f1`
FROM B
INNER JOIN A ON (B.fk_a = A.id)
LEFT JOIN C ON (B.id = C.fk_b);
Code above was tested and worked fine on MySQL 5.6
It appears that you have cartesian join.
You could try just putting DISTINCT infront of you column definition or you could so subqueries to link the tables together.
Try
SELECT DISTINCT A.f1
A.f2
B.f1
C.f1
D.f1
from A LEFT JOIN (B INNER JOIN D on D.fk_b = B.id) on A.id = B.fk_a
LEFT JOIN C on C.fk_b = B.id
You can do this:
B INNER JOIN TOP 1 D on.....
Like this
B INNER JOIN (SELECT TOP 1 fields from table) D ON ...
Try this:
SELECT DISTINCT A.f1
A.f2
B.f1
C.f1
D.f1
from A
LEFT JOIN (B INNER JOIN (SELECT DISTINCT fk_b FROM D) D on D.fk_b = B.id)
on A.id = B.fk_a
LEFT JOIN C on C.fk_b = B.id

Does not Recognize Column in Where Clause when Joining Tables

SELECT * FROM a
JOIN (SELECT * FROM b WHERE b.aId = a.Id) AS c ON c.aId = a.Id
It says does not recognize: a.Id in the Where Clause.
I know its probably cause im using a temp table and a.Id cannot be passed through but is there any way we can do that?
Because here is what actually happens
SELECT *
FROM a
JOIN (SELECT * FROM b
WHERE b.aId = a.Id
ORDER BY b.dateReg DESC
LIMIT 1) AS c ON c.aId = a.Id
I need the ORDER BY b.dateReg DESC LIMIT 1 as it returns me the last row that assosiates with the a Table.. If you require i can post the Create Query
-- find last rows on b
select * from b x
where exists(
select id
from b y
where y.id = b.id
having max(y.dateReg) = x.dateReg
group by id
)
-- then join that b to a, this is the final query:
select * from a
join
(
select * from b x
where exists(
select id
from b y
where y.id = b.id
having max(y.dateReg) = x.dateReg
group by id
)
) as last_rows on last_rows.id = a.id
-- simpler:
select *
from a join b x on a.id = x.id
where exists(
select id
from b y
where y.id = b.id
having max(y.dateReg) = x.dateReg
group by id)
-- or if you will use postgres:
select DISTINCT ON (a.id) *
from a join b x on a.id = x.id
order by a.id, b.dateReg DESC
-- look ma! no group by!
-- nothing beats postgresql's simplicity :-)
Try:
SELECT DISTINCT *
FROM A
JOIN B b ON b.aid = a.id
JOIN (SELECT b.aid,
MAX(b.datereg) 'max_datereg'
FROM B b
GROUP BY b.aid) md ON md.aid = b.aid
AND md.max_datereg = b.datereg
If you do want the first record associated with the associate, use:
SELECT DISTINCT *
FROM A
JOIN B b ON b.aid = a.id
JOIN (SELECT b.aid,
MIN(b.datereg) 'min_datereg'
FROM B b
GROUP BY b.aid) md ON md.aid = b.aid
AND md.min_datereg = b.datereg