Select several latest values for different IDs - sql

I have the following table:
serialNumber value masterID
1 red 500
1 blue 501
2 red 502
2 green 503
2 black 504
I need a single query that returns the following
serialNumber value
1 blue
2 black
I mean, I need the latest data that was inserted in the table for each serialNumber. MasterID is an incremental identity (PK).
I only managed to retrieve the last value for a single row (using top 1 and order by masterid desc) but I need to retrieve lot of data and making a query for each serialNumber is not viable.
Thanks!

You can select your Max(MasterID) from a subquery, then join the results back to the table to get the value for that max(masterId):
SELECT t1.serialNumber,
value,
masterID
FROM TABLE t1
INNER JOIN (
SELECT MAX(masterID) AS maxid,
serialNumber
FROM TABLE
GROUP BY serialNumber
) t2
ON t1.masterid = t2.maxid
AND t1.serialNumber = t2.serialNumber

If you are using SQL SERVER then try this.
;WITH cte
AS (SELECT Row_number()
OVER(
partition BY serialNumber
ORDER BY masterID DESC) rn,
serialNumber,
value,
masterID)
SELECT serialNumber,
value
FROM cte
WHERE rn = 1

Related

How to group and pick only certain values based on a field using select query SQL

I have a table as follow
ID
ORDERNO
1
123
1
123
2
456
2
456
During every select query done via application using JDBC, only the grouped records based on ORDERNO should be picked.
That means, for example, during first select query only details related to ID = 1, but we cannot specify the ID number in where clause because we do not know how many number of IDs will be there in future. So the query should yield only one set of records; application will delete those records after picking, hence next select query will result in picking other set of records. How to achieve it?
You can use TOP WITH TIES for this
SELECT TOP (1) WITH TIES
t.ID,
t.ORDERNO
FROM YourTable t
ORDER BY
t.ID;
If you want to select and delete at the same time you could delete using an OUTPUT clause
WITH cte AS (
SELECT TOP (1) WITH TIES
t.ID,
t.ORDERNO
FROM YourTable t
ORDER BY
t.ID
)
DELETE cte
OUTPUT deleted.*;
As one option you could select on the MIN(ID) like:
SELECT *
FROM yourtable
WHERE ID = (SELECT MIN(ID) FROM yourtable);
You could also use window functions to do this:
SELECT ID, ORDERNO
FROM
(
SELECT ID, ORDERNO
DENSE_RANK() OVER (ORDER BY ID ASC) AS dr
FROM yourtable
)dt
WHERE dr = 1;
order your rows and select top n number of rows that you want :
select top (1) with ties ID, ORDERNO
from tablename
order by ID asc

SQL MIN(value) matching row in PostgreSQL

I have a following tables:
TABLE A:
ID ID NAME PRICE CODE
00001 B 1000 1
00002 A 2000 1
00003 C 3000 1
Here is the SQL I use:
Select Min (ID),
Min (ID NAME),
Sum(PRICE)
From A
GROUP BY CODE
Here is what I get:
ID ID NAME PRICE
00001 A 6000
As you can see, ID NAME don't match up with the min row value. I need them to match up.
I would like the query to return the following
ID ID NAME PRICE
00001 B 6000
What SQL can I use to get that result?
If you want one row, use limit or fetch first 1 row only:
select a.*
from a
order by a.price asc
fetch first 1 row only;
If, for some reason, you want the sum() of all prices, then you can use window functions:
select a.*, sum(a.price) over () as sum_prices
from a
order by a.price asc
fetch first 1 row only;
You can use row_number() function :
select min(id), max(case when seq = 1 then id_name end) as id_name, sum(price) as price, code
from (select t.*, row_number() over (partition by code order by id) seq
from table t
) t
group by code;
you can also use sub-query
select t1.*,t2.* from
(select ID,Name from t where ID= (select min(ID) from t)
) as t1
cross join (select sum(Price) as total from t) as t2
https://dbfiddle.uk/?rdbms=postgres_10&fiddle=a496232b552390a641c0e5c0fae791d1
id name total
1 B 6000

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

SQL: JOIN two tables with distinct rows from one table

This might be a very simple problem but I can't seem to get my head around this since last night.
I have 3 tables
VirtualLicense
VirtualLicenseId ProductName
-----------------------------------
1 Transaction
2 Query
3 Transaction
Product
ProductId Name
---------------------------
1 Transaction
2 Query
License
LicenseId ExpiryDate ProductId
-----------------------------------------
1 14/07/2013 1
2 13/07/2013 1
3 13/07/2013 2
4 14/07/2013 2
The VirtualLicense and License are joined using ProductName and ProductId mapping using the Product table.
I want to get combination of VirtualLicenseId and LicenseId, where I can basically assign the VirtualLicenseId to a LicenseId. Once a licenseid is assigned to a VirtualLicenseId, it should not be available for the following VirtualLicenseIds. Also, I want that the licenseid for which the expirydate is nearer(smaller) should be assigned first.
So, the result for my example data set should be
VirtualLicenseId LicenseId
---------------------------------
1 2
2 3
3 1
I do not want to loop over any of the tables for this.
I hope my problem is clear from my description and data.
You can do something like this:
In first CTE - assign rankings for VirtualLicenses within the Product groups.
In second CTE - assign rankings for Licensce within the Product groups (order by exp. date)
And at the end just join the two subqueries on productID and ranking.
WITH CTE_VL AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY vl.VirtualLicenseId ASC) RN
FROM dbo.VirtualLicense vl
LEFT JOIN dbo.Product p ON vl.ProductName = p.Name
)
,CTE_License AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY ExpiryDate ASC) RN
FROM dbo.License
)
SELECT VirtualLicenseId, LicenseId
FROM CTE_VL vl
LEFT JOIN CTE_License l ON vl.ProductId = l.ProductID AND vl.RN = l.RN
SQLFiddle DEMO

Select column value where other column is max of group

I am trying to select two columns into a table (ID and state). The table should show the state with the maximum value for each ID. I've tried a few other examples but nothing seems to work.
Original data structure:
ID state value (FLOAT)
1 TX 921,294,481
1 SC 21,417,296
1 FL 1,378,132,290
1 AL 132,556,895
1 NC 288,176
1 GA 1,270,986,631
2 FL 551,374,452
2 LA 236,645,530
2 MS 2,524,536,050
2 AL 4,128,682,333
2 FL 1,503,991,028
The resulting data structure should therefore look like this:
ID STATE (Max Value)
1 FL
2 AL
Florida and Alabama having the largest values in their ID groups.
Any help would be greatly appreciated on this. I did find a SO answer here already, but could not make the answers work for me.
For SQL Server (and other products with windowed functions):
SELECT *
FROM
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY value desc) as rn
FROM
UnnamedTable
) t
WHERE
t.rn = 1
You can use a subquery to get this result:
select t1.id, t1.[state] MaxValue
from yourtable t1
inner join
(
select id, max(value) MaxVal
from yourtable
group by id
) t2
on t1.id = t2.id
and t1.value = t2.maxval
order by t1.id
See SQL Fiddle with Demo
A solution, based on the assumption that value is numeric:
SELECT
[ID],
[State],
[Value]
FROM
(
SELECT
[ID],
[State],
[Value],
Rank() OVER (PARTITION BY [ID] ORDER BY [Value] DESC) AS [Rank]
FROM [t1]
) AS [sub]
WHERE [sub].[Rank] = 1
ORDER BY
[ID] ASC,
[State] ASC
If multiple States with the same ID have the same Value, they would all get the same Rank. This is different from using Row_Number, which return unique row numbers, but the order is chosen arbitrarily. (See also: SQL RANK() versus ROW_NUMBER())