Table join; select 1 entry from join - sql

In SQL Server, I have two tables. One "main" table with all the data and a unique id per entry. The other table is an audit log, where that id from main will be stored multiple times in the audit table.
My query looks like:
SELECT
a.title, a.id, a.name, t.user, t.time
FROM
MainTable a
INNER JOIN
AuditLog AS t ON a.id = t.id
WHERE
a.NAME LIKE 'Something%'
AND a.ACTIVE = 'Y'
Which gives a result like:
TITLE ID NAME USER TIME
----------------------------------------------------------------
Something1 someth1 Some 1 User5 468534771
Something1 someth1 Some 1 User7 468574887
Something2 someth2 Some 2 User6 468584792
Which returns multiple results of the ID. I only want the oldest (from AuditLog) entry and not every one. So the result I would want looks like:
TITLE ID NAME USER TIME
----------------------------------------------------------------
Something1 someth1 Some 1 User5 468534771
Something2 someth2 Some 2 User6 468584792
How can this be done? I'm trying some subqueries within the join.

In sql server 2005+, you can use row_number()
select title, id, name, user, time
from
(
SELECT a.title, a.id, a.name, t.user, t.time,
row_number() over(partition by a.id order by t.time) rn
FROM MainTable a
INNER JOIN AuditLog AS t
ON a.id = t.id
WHERE a.NAME LIKE 'Something%'
AND a.ACTIVE='Y'
) src
where rn = 1
See SQL Fiddle with Demo
Or you can use a sub-query to get the min time:
SELECT a.title, a.id, a.name, l.user, l.time
FROM MainTable a
INNER JOIN AuditLog l
ON a.id = l.id
INNER JOIN
(
select min(time) Mintime, id
from AuditLog
group by id
) AS t
ON l.id = t.id
AND l.time = t.mintime
WHERE a.NAME LIKE 'Something%'
AND a.ACTIVE='Y';

SELECT TOP 1 a.title, a.id, a.name, t.user, t.time
FROM MainTable a
INNER JOIN AuditLog AS t
ON a.id = t.id
WHERE a.NAME LIKE 'Something%'
AND a.ACTIVE='Y'
ORDER BY T.DATE DESC

You could use outer apply, like:
select *
from MainTable a
outer apply
(
select top 1 *
from AuditLog t
where a.id = t.id
order by
t.time
) t
where a.name like 'Something%'
and a.active = 'y'

Give this a try
SELECT a.title, a.id, a.name, t.user, t.time
FROM MainTable a
JOIN AuditLog AS t ON a.id = t.id
JOIN (SELECT ID, MAX(TIME) AS LASTTIME FROM AUDITLOG) AS c
ON c.ID = t.id AND c.LASTTIME = t.time
WHERE a.NAME LIKE 'Something%'
AND a.ACTIVE = 'Y'

Related

Removing duplicate values from a column in SQL

I have two tables A (group_id, id, subject) and B (id, date). Below is the joint table of tables A and B on id. I have tried using distinct and partition to remove the duplicates in group_id(field) only, but no luck:
My code:
select
a.group_id, a.id, a.subject, b.date
from
A a
inner join
(select
b.*,
row_number() over (partition by group_id order by date asc) as seqnum
from
B b) b on a.id = b.id and seqnum = 1
order by
date desc;
I got this error when I ran the code:
Partitioning can not be used stand-alone in query near 'partition by group_id order by date asc) as seqnum from B' at line 1
This is my expected result:
Thank you in advance!
It looks like you want the earliest date for each row in the table you show. Your question mentions two tables, but you only show one.
I recommend a correlated subquery in most databases:
select b.*
from b
where b.date = (select min(b2.date)
from b b2
where b2.group_id = b.group_id
);
I see. You need to join first and then use row_number():
select ab.*
from (select a.group_id, a.id, a.subject, b.date,
row_number() over (partition by a.group_id order by b.date) as seqnum
from A a join
B b
on a.id = b.id
) ab
where seqnum = 1
order by date desc;
You are almost there. But the column that you try to use to partition (ie group_id) comes from table a, which is not available in the subquery.
You would need to JOIN and assign the row number in a subquery, and then filter in the outer query.
select *
from (
select
a.group_id,
a.id,
a.subject,
b.date,
row_number() over (partition by a.group_id order by b.date asc) as seqnum
from a
inner join b on ON a.id = b.id
)
where seqnum = 1
ORDER BY date desc;
Another way to achieve your goal though it may not be the efficient one
SELECT
A.group_id, A.id, B.Date, A.subject
FROM A
INNER JOIN B
ON A.Id = B.Id
INNER JOIN
(
SELECT
A.Group_id, MIN(B.Date) AS Date
FROM A
INNER JOIN B
ON A.Id = B.Id
GROUP BY A.group_id
) AS supportTable
ON A.group_id = supportTable.group_id
AND B.Date = supportTable.Date

SQL Query Join two Queries with Sum()

I know this is simple for you sql people, but I am trying to combine two simple queries into one. It's probably obvious from the code that job.ActiveJob.ID = job.EstimateTbl.ID_Job.
I'm trying to get: id, JobName, sum(itemAmount)
Thanks for your help.
SELECT a.id, a.JobName
FROM job.ActiveJobsTbl AS a
WHERE a.ID = '100'
SELECT SUM(itemAmount)
FROM job.EstimateTbl
WHERE ID_Job = '100'
You can JOIN the tables by the ON E.ID_Job = A.ID and get the values in SELECT with GROUP BY
SELECT A.id, A.JobName, SUM(E.itemAmount) AS Amount
FROM job.ActiveJobsTbl AS A
INNER JOIN job.EstimateTbl E ON E.ID_Job = A.ID
WHERE A.ID = '100'
GROUP BY A.id, A.JobName;
using LEFT JOIN
SELECT a.id, a.JobName,EstimateTbl.itemAmount
FROM job.ActiveJobsTbl AS a
left join
(
SELECT SUM(itemAmount) itemAmount,ID_Job
FROM job.EstimateTbl
group by ID_Job
) EstimateTbl on EstimateTbl.ID_Job = a.ID
WHERE a.ID = '100'
You can use APPLY :
select a.id, a.JobName, t.itemAmount
from job.ActiveJobsTbl AS a OUTER APPLY
(SELECT SUM(t.itemAmount) AS itemAmount
FROM job.EstimateTbl AS t
WHERE t.ID_Job = a.ID
) t
WHERE a.ID = 100;
Try this:
SELECT A.id, A.JobName, SUM(E.itemAmount) AS Amount
FROM job.ActiveJobsTbl AS A
INNER JOIN job.EstimateTbl E ON E.ID_Job = A.ID
WHERE A.ID = '100'
GROUP BY A.id, A.JobName;

ORA-01799 - need to correct query

Not sure how to rewrite the below query. I’m trying to join table_a to the most recent table_b record. Currently testing for only one ID, but a different criteria on table_a may be added:
Select t.*
from table_a t
left join table_b d on d.id = T.id and d.MOD_DATE IN (SELECT MAX(mod_date) FROM table_b d2 WHERE d2.id = t.id)
where T.id = 123456
Any suggestions?
I think you are looking for something like:
SELECT t.*
FROM table_a t
LEFT JOIN (
SELECT d.*
FROM table_b d
INNER JOIN (
SELECT id
, MAX(mod_date) mod_date_max
FROM table_b d2
GROUP BY id
) db
ON db.id = d.id
AND db.mod_date_max = d.mod_date
) d
ON d.id = T.id
WHERE T.id = 123456
Note that your where clause turns the left join into an inner join.
Also, if you get an error, please post the error message as well, not just its number.
I also found the same could be achieved with the following query:
SELECT * FROM table_a t
WHERE id IN (
SELECT id
FROM (
SELECT id,MAX(MOD_DATE)
FROM table_b
WHERE id = 123456
GROUP BY id
)
)

sql query getting ID wise balance

I have a following SQL Query:(Oracle 10)
SELECT
A.ID,
SUM(A.AMT_CR) - SUM(A.AMT_DR) AS balAmt,
P.NAME
FROM TABLE1 A, TABLE2 P
WHERE P.ID BETWEEN 'C0100' AND 'C0200'
AND P.ID = A.ID
AND A.TRANS_DATE < '01-FEB-2013'
GROUP BY A.ID, P.NAME
ORDER BY A.ID
Above query executes nicely, but this query has a condition AND A.TRANS_DATE < '01-FEB-2013'. This condition is for getting the balAmt before the TRANS_DATE. But the issue with this query is that it only gets the IDs with respect to AND A.TRANS_DATE < '01-FEB-2013' condition. If a ID doesn't have TRANS_DATE less than '01-FEB-2013' then it doesn't show the record for that ID.
So I want to get All IDs from database. But I want to get balAmt with respect to AND A.TRANS_DATE < '01-FEB-2013' condition..
Any suggestion will be appreciated.
Thanks in advance.
SELECT
A.ID,
SUM(case when A.TRANS_DATE < '01-FEB-2013'
then nvl(A.AMT_CR,0) - nvl(A.AMT_DR,0)
else 0
end) AS balAmt,
P.NAME
FROM TABLE1 A, TABLE2 P
WHERE P.ID BETWEEN 'C0100' AND 'C0200'
AND P.ID = A.ID
GROUP BY A.ID, P.NAME
ORDER BY A.ID
SELECT Q.ID, W.balmt, W.NAME
FROM TABLE1 Q
LEFT JOIN (
SELECT
A.ID,
SUM(A.AMT_CR) - SUM(A.AMT_DR) AS balAmt,
P.NAME
FROM TABLE1 A, TABLE2 P
WHERE P.ID BETWEEN 'C0100' AND 'C0200'
AND P.ID = A.ID
AND A.TRANS_DATE < '01-FEB-2013'
GROUP BY A.ID, P.NAME
) W
ON Q.ID = W.ID
ORDER BY A.ID
This should do:
SELECT
A.ID,
SUM(A.AMT_CR) - SUM(A.AMT_DR) AS balAmt,
P.NAME
FROM TABLE1 A
LEFT JOIN TABLE2 P ON P.ID = A.ID
AND A.TRANS_DATE < '01-FEB-2013'
AND P.ID BETWEEN 'C0100' AND 'C0200'
GROUP BY A.ID, P.NAME
ORDER BY A.ID

Getting MIN date

I have a table(A) that looks something like:
ID Date
1 2012/01/12
2 2012/01/01
3 2012/01/03
4 2012/03/12
If I wanted to grab the MIN date for this query, would I just group by?
select
a.ID,
MIN(a.DATE),
b.name,
c.price
FROM
tablea a inner join tableb b on a.ID = b.ID
inner join tablec c b.ID = c.ID
You want a window function. The correct expression is:
select a.id,
min(a.date) over () as mindate,
b.name, c.price
. . .
This says to get the min of the date over the data. There is no partition, so it gets it over all the data.
If you are looking for those that had the minimum date, then you can do this:
select
a.ID,
a.DATE,
b.name,
c.price
FROM tablea a
INNER JOIN
(
SELECT Id, MIN(Date) AS MinDate
FROM tablea
GROUP BY Id
) As minA ON a.date = mina.mindate AND a.id = mina.id
inner join tableb b on a.ID = b.ID
inner join tablec c b.ID = c.ID
WITH recordList
as
(
select a.ID,
a.DATE,
b.name,
c.price,
DENSE_RANK() OVER (PARTITION BY a.ID
ORDER BY a.Date ASC) rn
FROM tablea a
inner join tableb b on a.ID = b.ID
inner join tablec c b.ID = c.ID
)
SELECT ID, DATE, name, Price
FROM recordList
WHERE rn = 1