How to join table to itself and select max values in SQL - sql

I have a contracts table:
contractId date price partId
1 20120121 10 1
2 20110130 9 1
3 20130101 15 2
4 20110101 20 2
The contract with greatest date being the active contract (don't blame me, I blame infor for creating xpps)
I need to create query to see only active contracts (one contract per part, the contract with highest date).
So the result of the query should be like this:
contractId date price partId
1 20120121 10 1
3 20130101 15 2
I am out of ideas here, I tried self joining the table, I tried aggregation functions, but I can't figure it out. If anyone would have any idea, please share them with me..

this will work on almost all RDBMs,
SELECT a.*
FROM tableName A
INNER JOIN
(
SELECT partID, MAX(date) maxDate
FROM tableName
GROUP BY partID
) B on a.partID = b.partID AND
a.date = b.maxDate
SQLFiddle Demo
if your RDBMS supports Window Function,
SELECT contractId ,date, price,partId
FROM
(
SELECT contractId ,date, price,partId,
ROW_NUMBER() OVER (PARTITION BY PartID
ORDER BY date DESC) rn
FROM tableName
) s
WHERE rn = 1
SQLFiddle Demo

SELECT c.*
FROM contracts c
INNER JOIN
(
SELECT partId, MAX([date]) AS MaxDate
FROM contracts
GROUP BY partID
) MaxDate
ON c.partId = MaxDate.partID
AND c.[date] = MaxDate.[date]

This is the fast self join:
SELECT c1.* FROM contracts c1
LEFT OUTER JOIN contracts c2 ON c2.partId = 1.partId AND c1.date < c2.date
WHERE c2.contractId IS NULL
The use of sub selects (nested selects) tend to be slower.

Related

Select multiple max values after GROUP BY query

Suppose I have a table look like this:
date ID income
0 9/1 C 10.40
1 9/3 A 33.90
2 9/3 B 29.10
3 9/4 C 19.30
4 9/4 B 17.80
5 9/5 B 9.55
6 9/5 C 11.10
7 9/5 A 13.10
8 9/7 A 29.10
9 9/7 B 29.10
I want to find out the ID who made the most income for each date. The most intuitive approach would be writing
SELECT ID, MAX(income) FROM table GROUP BY date
But there are two IDs who made the same MAX income on 9/7, I want to retain all ties on the same date, by using that query I will ignore one ID on 9/7, and 29.1 appears on 9/3 and 9/7, any other approach?
A join based approach doesn't have this problem, and would retain all records tied for the max income on a given date.
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT date, MAX(income) AS max_income
FROM yourTable
GROUP BY date
) t2
ON t1.date = t2.date AND t1.income = t2.max_income
ORDER BY
t1.date;
The way the above query works is to join the complete original table to a subquery which finds, for each date, the maximum income value. This has the effect of filtering off any record which did not have the max income on a given date. Pay close attention to the join condition, which has two components, the date, and the income.
If your database supports analytic function, we can also use RANK here:
SELECT date, ID, income
FROM
(
SELECT t.*, RANK() OVER (PARTITION BY date ORDER BY income DESC) rnk
FROM yourTable t
) t
WHERE rnk = 1
ORDER BY date;
one approach can be like below
with cte1
(
Select t1.*
FROM yourTable t1
INNER JOIN
(
SELECT date, MAX(income) AS max_income
FROM yourTable
GROUP BY date
) t2
ON t1.date = t2.date AND t1.income = t2.max_income
) select min(ID) as ID, date,income from cte1
group by date,income
As you not mentioned which id you need in case of two ID's(when income is same on a particular date) so i took minimum id among them when two id's income is same on a particular date But at the same time you may use max() function also
Try below using subquery and as you've tie for one date so take minimum ID which'll give you one id from date 9/7
select date,min(ID),income
from
(SELECT t1.date, t1.ID,t1.income
FROM tablename t1
INNER JOIN
(
SELECT date, MAX(income) AS mincome
FROM yourTable
GROUP BY date
) t2 ON t1.date = t2.date AND t1.income = t2.mincome
)X group by date,income

SQL Server query distinct

I'm trying to do a query in SQL Server 2008. This is the structure of my table
Recno ClientNo ServiceNo
---------------------------------
1234 17 27
2345 19 34
3456 20 33
4567 17 34
I'm trying to select RecNo however, filtering by distinct ClientNo, so for some clients such as client no 17 - they have more than 1 entry, I'm trying to count that client only once. So basically, looking at this table, I'm only supposed to see 3 RecNo's, since there are only 3 distinct clients. Please help
Select RecNo, Count(ClientNo)
from TblA
where Count(clientNo)<2
Something like this?
EDIT:
The value of RecNo is not relevant, I only need to have an accurate number of records. In this case, I'd like to have 3 records.
oaky you are getting some crazy answers probably becuase your desired result is not clear so I suggest if some of these are not what you need that you clarify your desired result.
If you want the answer 3, I can only assume you want a count of DISTINCT ClientNo's if so it is simply aggregation.
SELECT COUNT(DISTINCT ClientNo) as ClientNoDistinctCount
FROM
TblA
GROUP BY
ClientNo
Ok, this will give you the count that you want:
WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY ClientNo ORDER BY Recno)
FROM TblA
)
SELECT COUNT(DISTINCT Recno) N
FROM CTE
WHERE RN = 1;
Try this..
;with cte1
As(SELECT Recno,clientno
,row_number() over(partition by clientno order by Recno )RNO FROM TblA)
Select Recno,clientno
From cre1 where RNO=1
Choose only ClientNo having the max Recno (or replace < with > to choose the min one).
Select *
from TblA t1
where not exists(select 1
from TblA t2
where t1.ClientNo = t2.ClientNo and t1.Recno < t2.Recno )
BTW, the other solution already mentioned, utilizing row_number() needs no CTE in this case
SELECT TOP(1) WITH TIES *
FROM TblA
ORDER BY ROW_NUMBER() OVER(PARTITION BY ClientNo ORDER BY Recno)

Custom GROUP BY clause

SAMPLE DATA
Suppose I have table like this:
No Company Vendor Code Date
1 C1 V1 C1 2016-03-08
1 C1 V1 C1 2016-03-07
1 C1 V1 C2 2016-03-06
DESIRED OUPUT
Desired output should be:
No Company Vendor Code Date
1 C1 V1 C1 2016-03-08
It should take max Date for No, Company, Vendor (group by these columns). But shouldn't group by Code, It have to be taken for that Date.
QUERY
SQL query like:
.....
LEFT JOIN (
SELECT No_, Company, Vendor, Code, MAX(Date)
FROM tbl
GROUP BY No_, Company, Vendor, Code
) t2 ON t1.Company = t2.Company and t1.No_ = t2.No_
.....
OUTPUT FOR NOW
But I got output for now:
No Company Vendor Code Date
1 C1 V1 C1 2016-03-08
1 C1 V1 C2 2016-03-06
That because Code records are different, but It should take C1 code in this case (because No, Company, Vendor match)
WHAT I'VE TRIED
I've tried to remove Code from GROUP BY clause and use SELECT MAX(Code)..., but this is wrong that because It take higher Code by alphabetic.
Have you ideas how can I achieve It? If something not clear I can explain more.
If you don't have any identity column for your table then each row is identified by all column values combination it has. That brings us weird on statement. It includes all columns we are grouping by and a date column which is max for given tuple (No_, Company, Vendor).
select t1.No_, t1.Company, t1.Vendor, t1.Code, t1.Date
from tbl t1
join (select No_, Company, Vendor, MAX(Date) as Date
from tbl
group by No_, Company, Vendor) t2
on t1.No_ = t2.No_ and
t1.Company = t2.Company and
t1.Vendor = t2.Vendor and
t1.Date = t2.Date
Take a look at this similar question.
Edit
Thank you for an answer, but this returning duplicates. Suppose that there can be rows with equal No, Company, Vendor and Date, some other columns are different, but no care. So with INNER SELECT everything fine, It returning distinct values, but problem accured when joining t1, that because It have multiple values.
Then you might be interested in such tsql constructions as rank or row_number. Take a look at Ullas' answer. Try rank as well as it can give slightly different output which might fit your needs.
You could give a row_number partitioned by No, Vendor and Date and order by descending order of date.
Query
;with cte as (
select rn = row_number() over(
partition by [No], Company, Vendor
order by [Date] desc
), *
from tbl
)
select [No], Company, Vendor, Code, [Date] from cte
where rn = 1;
If 1 date only can have 1 record, then you can Query it by search the max date first, then check it.
select No_, Company, Vendor, Code, Date
FROM tbl
where Date in
(select MAX(Date) from tbl GROUP BY No_, Company, Vendor)
if there is more than 1 row that could have the same date, then you could use partition
with cte as
(
select *, ROW_NUMBER() over(partition by No_, Company, Vendor order by Date DESC) as rn
from tbl
)
select No_, Company, Vendor, Code, Date
from cte
where rn=1
A Common Table Expression will do it for you.
WITH cte(N,C,V,D)
AS
(
SELECT t1.[No]
,t1.[Company]
,t1.[Vendor]
,MAX(t1.[Date])
FROM [MyTest] t1
GROUP BY t1.[No]
,t1.[Company]
,t1.[Vendor]
)
SELECT N,C,V,t2.Code,D
FROM cte c
INNER JOIN MyTest t2 ON c.N = t2.No AND c.C = t2.Company AND c.V = t2.Vendor AND c.D = t2.Date

How to show different rows of same column as two columns in Select statement?

We have a table say 'timekeeper' like
id | entry_date
------------------
1 | 1406864087263
1 | 1406864087268
Assume the entry_date column represents in and out time. Instead of put two columns for in and out we used one column. This is wrong design. I accept.
But my requirement is i want select query to produce output like,
id | in_time | out_time
1 | 1406864087263 | 1406864087268
i.e., show same column as two columns in select stmt.
Is it possible to achieve?
Assumes you always enter before you exit and there is only one enter and exit per id.
SELECT id,
Min(entry_time) [in_time],
Max(entry_time) [out_time]
FROM timekeeper
GROUP BY id
Link to a sqlfiddle: http://sqlfiddle.com/#!6/a93f5/2
You can create a rownumber with 1 for in_rows and 2 for out_rows and self join the in and out rows
with table_with_rows as
(
select *, ROW_NUMBER() OVER ( PARTITION BY id ORDER BY entry_date )
as in_out from timekeeper
)
select in_table.id, in_table.entry_date as in_time, out_table.entry_date
as out_time from
table_with_rows in_table
inner join
table_with_rows out_table
on in_table.in_out = 1 and out_table.in_out = 2 and in_table.id = out_table.id
Link to SqlFiddle
;WITH CTE
AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY LOGINOUT)RNO,ID,LOGINOUT FROM #TEMP
)
SELECT C1.LOGINOUT AS INTIME,C2.LOGINOUT AS OUTTIME FROM CTE C1
LEFT OUTER JOIN CTE AS C2
ON C1.ID = C2.ID+1

Join two queries from the same table - SELECT DISTINCT?

I have two tables linked by an AUTO_KEY field, from one table I'm retrieving the number (id), from the other I get several statuses by number(id), each status has a date associated to it.
I need to restrict the results only to the maximum/latest date for all numbers(ids) and the corresponding status
SELECT
OPERATION.NUMBER,
STATUS.STATUS,
Max(STATUS.DATE)
FROM
STATUS,
OPERATION
WHERE
OPERATION.AUTO_KEY = STATUS.AUTO_KEY
From here
Number Status Date
-----------------------------
1 A 10/20/13
1 B 10/15/13
2 A 10/10/13
2 AX 10/05/13
2 AD 10/03/13
3 DD 10/03/13
The outcome should be
Number Status Date
-----------------------------
1 A 10/20/13
2 A 10/10/13
3 DD 10/03/13
Thanks in advance
You can use a CTE with ROW_NUMBER() function. Also Please use a Table JOIN instead FROM STATUS, OPERATION
;With CTE AS (
SELECT O.NUMBER, S.STATUS, S.DATE,
ROW_NUMBER() OVER (ORDER BY S.DATE DESC) RN
FROM STATUS S JOIN OPERATION O
ON O.AUTO_KEY = S.AUTO_KEY
)
SELECT NUMBER, STATUS, DATE
FROM CTE
WHERE RN = 1
ORDER BY NUMBER
SELECT OPERATION.CNUMBER,
STATUS.STATUS,
STATUS.CDATE
FROM STATUS,
OPERATION
WHERE OPERATION.AUTO_KEY = STATUS.AUTO_KEY
AND STATUS.CDATE = (
SELECT MAX(STATUS.CDATE) MAX_DATE
FROM STATUS,
OPERATION
WHERE OPERATION.AUTO_KEY = STATUS.AUTO_KEY
GROUP BY OPERATION.CNUMBER )