SQL query in ORACLE - select most recent date - sql

I am a newbie in the world of SQL query.
I need to eliminate the duplicate Staff # and retrieve only the highlighted row.
Any help is highly appreciated.
Staff# Pay_DT Due_DT loan_flag housing
------------------------------------------------------------------------
123-45-6789 14-Feb-14 3-Jan-14 Y null
123-45-6789 14-Feb-14 3-Jan-14 Y Annual
123-45-6789 14-Feb-14 13-Jan-14 Y null
**123-45-6789 14-Feb-14 13-Jan-14 Y Annual**
123-45-6789 null null Y null
123-45-6789 null null Y Annual

Perhaps you want
SELECT *
FROM (SELECT a.*,
rank() over (partition by staff# order by pay_dt desc) rnk
FROM table_name a
WHERE housing = 'Annual')
WHERE rnk = 1
Alternately
SELECT *
FROM (SELECT a.*,
max(pay_dt) over (partition by staff#) max_pay_dt
FROM table_name a
WHERE housing = 'Annual')
WHERE pay_dt = max_pay_dt
If you can have ties (two rows with the same pay_dt where the housing column has a value of Annual for a particular Staff# value, both of these queries would return all the rows with that condition. If you want to break the tie arbitrarily, you could use row_number rather than rank. Otherwise, you'd need to tell us what logic you'd want to use to break the ties.

Related

Complex SQL query or queries

I looked at other examples, but I don't know enough about SQL to adapt it to my needs. I have a table that looks like this:
ID Month NAME COUNT First LAST TOTAL
------------------------------------------------------
1 JAN2013 fred 4
2 MAR2013 fred 5
3 APR2014 fred 1
4 JAN2013 Tom 6
5 MAR2014 Tom 1
6 APR2014 Tom 1
This could be in separate queries, but I need 'First' to equal the first month that a particular name is used, so every row with fred would have JAN2013 in the first field for example. I need the 'Last" column to equal the month of the last record of each name, and finally I need the 'total' column to be the sum of all the counts for each name, so in each row that had fred the total would be 10 in this sample data. This is over my head. Can one of you assist?
This is crude but should do the trick. I renamed your fields a bit because you are using a bunch of "RESERVED" sql words and that is bad form.
;WITH cte as
(
Select
[NAME]
,[nmCOUNT]
,ROW_NUMBER() over (partition by NAME order by txtMONTH ASC) as 'FirstMonth'
,ROW_NUMBER() over (partition by NAME order by txtMONTH DESC) as 'LastMonth'
,SUM([nmCOUNT]) as 'TotNameCount'
From Table
Group by NAME, [nmCOUNT]
)
,cteFirst as
(
Select
NAME
,[nmCOUNT]
,[TotNameCount]
,[txtMONTH] as 'ansFirst'
From cte
Where FirstMonth = 1
)
,cteLast as
(
Select
NAME
,[txtMONTH] as 'ansLast'
From cte
Where LastMonth = 1
Select c.NAME, c.nmCount, c.ansFirst, l.ansLast, c.TotNameCount
From cteFirst c
LEFT JOIN cteLast l on c.NAME = l.NAME

SQL query with grouping and MAX

I have a table that looks like the following but also has more columns that are not needed for this instance.
ID DATE Random
-- -------- ---------
1 4/12/2015 2
2 4/15/2015 2
3 3/12/2015 2
4 9/16/2015 3
5 1/12/2015 3
6 2/12/2015 3
ID is the primary key
Random is a foreign key but i am not actually using table it points to.
I am trying to design a query that groups the results by Random and Date and select the MAX Date within the grouping then gives me the associated ID.
IF i do the following query
select top 100 ID, Random, MAX(Date) from DateBase group by Random, Date, ID
I get duplicate Randoms since ID is the primary key and will always be unique.
The results i need would look something like this
ID DATE Random
-- -------- ---------
2 4/15/2015 2
4 9/16/2015 3
Also another question is there could be times where there are many of the same date. What will MAX do in that case?
You can use NOT EXISTS() :
SELECT * FROM YourTable t
WHERE NOT EXISTS(SELECT 1 FROM YourTable s
WHERE s.random = t.random
AND s.date > t.date)
This will select only those who doesn't have a bigger date for corresponding random value.
Can also be done using IN() :
SELECT * FROM YourTable t
WHERE (t.random,t.date) in (SELECT s.random,max(s.date)
FROM YourTable s
GROUP BY s.random)
Or with a join:
SELECT t.* FROM YourTable t
INNER JOIN (SELECT s.random,max(s.date) as max_date
FROM YourTable s
GROUP BY s.random) tt
ON(t.date = tt.max_date and s.random = t.random)
In SQL Server you could do something like the following,
select a.* from DateBase a inner join
(select Random,
MAX(dt) as dt from DateBase group by Random) as x
on a.dt =x.dt and a.random = x.random
This method will work in all versions of SQL as there are no vendor specifics (you'll need to format the dates using your vendor specific syntax)
You can do this in two stages:
The first step is to work out the max date for each random:
SELECT MAX(DateField) AS MaxDateField, Random
FROM Example
GROUP BY Random
Now you can join back onto your table to get the max ID for each combination:
SELECT MAX(e.ID) AS ID
,e.DateField AS DateField
,e.Random
FROM Example AS e
INNER JOIN (
SELECT MAX(DateField) AS MaxDateField, Random
FROM Example
GROUP BY Random
) data
ON data.MaxDateField = e.DateField
AND data.Random = e.Random
GROUP BY DateField, Random
SQL Fiddle example here: SQL Fiddle
To answer your second question:
If there are multiples of the same date, the MAX(e.ID) will simply choose the highest number. If you want the lowest, you can use MIN(e.ID) instead.

Select only Contiguous Records in DB2 SQL

So i have a table of readings (heavily simplified version below) - sometimes there is a break in the reading history (see the record i have flagged as N) - The 'From Read' should always match a previous 'To Read' or the 'To Read' should always match a later 'From Read' BUT I want to only select records as far back as the first 'break' in the reads.
How would i write a query in DB2 SQL to only return the rows flagged with a 'Y'?
EDIT: The contiguous flag is something i have added manually to represent the records i would like to select, it does not exist on the table.
ID From To Contiguous
ABC 01/01/2014 30/06/2014 Y
ABC 01/06/2013 01/01/2014 Y
ABC 01/05/2013 01/06/2013 Y
ABC 01/01/2013 01/02/2013 N
ABC 01/10/2012 01/01/2013 N
Thanks in advance!
J
you will need a recursive select
something like that:
WITH RECURSIVE
contiguous_intervals(start, end) AS (
select start, end
from intervals
where end = (select max(end) from intervals)
UNION ALL
select i.start, i.end
from contiguous_intervals m, intervals i
where i.end = m.start
)
select * from contiguous_intervals;
You can do this with lead(), lag(). I'm not sure what the exact logic is for your case, but I think it is something like:
select r.*,
(case when (prev_to = from or prev_to is null) and
(next_from = to or next_from is null)
then 'Y'
else 'N'
end) as Contiguous
from (select r.*, lead(from) over (partition by id order by from) as next_from,
lag(to) over (partition by id order by to) as prev_to
from readings r
) r;

Joining next Sequential Row

I am planing an SQL Statement right now and would need someone to look over my thougts.
This is my Table:
id stat period
--- ------- --------
1 10 1/1/2008
2 25 2/1/2008
3 5 3/1/2008
4 15 4/1/2008
5 30 5/1/2008
6 9 6/1/2008
7 22 7/1/2008
8 29 8/1/2008
Create Table
CREATE TABLE tbstats
(
id INT IDENTITY(1, 1) PRIMARY KEY,
stat INT NOT NULL,
period DATETIME NOT NULL
)
go
INSERT INTO tbstats
(stat,period)
SELECT 10,CONVERT(DATETIME, '20080101')
UNION ALL
SELECT 25,CONVERT(DATETIME, '20080102')
UNION ALL
SELECT 5,CONVERT(DATETIME, '20080103')
UNION ALL
SELECT 15,CONVERT(DATETIME, '20080104')
UNION ALL
SELECT 30,CONVERT(DATETIME, '20080105')
UNION ALL
SELECT 9,CONVERT(DATETIME, '20080106')
UNION ALL
SELECT 22,CONVERT(DATETIME, '20080107')
UNION ALL
SELECT 29,CONVERT(DATETIME, '20080108')
go
I want to calculate the difference between each statistic and the next, and then calculate the mean value of the 'gaps.'
Thougts:
I need to join each record with it's subsequent row. I can do that using the ever flexible joining syntax, thanks to the fact that I know the id field is an integer sequence with no gaps.
By aliasing the table I could incorporate it into the SQL query twice, then join them together in a staggered fashion by adding 1 to the id of the first aliased table. The first record in the table has an id of 1. 1 + 1 = 2 so it should join on the row with id of 2 in the second aliased table. And so on.
Now I would simply subtract one from the other.
Then I would use the ABS function to ensure that I always get positive integers as a result of the subtraction regardless of which side of the expression is the higher figure.
Is there an easier way to achieve what I want?
The lead analytic function should do the trick:
SELECT period, stat, stat - LEAD(stat) OVER (ORDER BY period) AS gap
FROM tbstats
The average value of the gaps can be done by calculating the difference between the first value and the last value and dividing by one less than the number of elements:
select sum(case when seqnum = num then stat else - stat end) / (max(num) - 1);
from (select period, row_number() over (order by period) as seqnum,
count(*) over () as num
from tbstats
) t
where seqnum = num or seqnum = 1;
Of course, you can also do the calculation using lead(), but this will also work in SQL Server 2005 and 2008.
By using Join also you achieve this
SELECT t1.period,
t1.stat,
t1.stat - t2.stat gap
FROM #tbstats t1
LEFT JOIN #tbstats t2
ON t1.id + 1 = t2.id
To calculate the difference between each statistic and the next, LEAD() and LAG() may be the simplest option. You provide an ORDER BY, and LEAD(something) returns the next something and LAG(something) returns the previous something in the given order.
select
x.id thisStatId,
LAG(x.id) OVER (ORDER BY x.id) lastStatId,
x.stat thisStatValue,
LAG(x.stat) OVER (ORDER BY x.id) lastStatValue,
x.stat - LAG(x.stat) OVER (ORDER BY x.id) diff
from tbStats x

Join two tables with same # of row but sorted for NULL

I need to join two tables with the same number of rows. Each table has 1 column. There is NO CONNECTING COLUMN to reference for a join. I need to join them side by side because each table was sorted separately so that numeric values are at the top in descinding order.
The Table Earners has income values from say 200K down to 0. I cannot just select using 2 cases, because then I will have my first row with Incomes above 100K, but the first 20 or so entries in the second row are NULL. I want the second row to also be sorted descending.
I looked up using ORDER BY within CASE but there is no such thing. I have tried to read about row_number() but none of the examples seem to match or make sense.
drop table #20plus
select
case
when Income >= 20000 AND Income < 100000
then Income end as 'mula'
into #20plus
from Earners
order by mula desc
drop table #100plus
select
case
when Income >= 100000
then Income end as 'dinero'
into #100plus
from Earners
order by dinero desc
Select A.dinero, B.mula
FROM #100plus as A JOIN #20plus as B
ON A.????? = B.?????
Since both A and B are sorted descending, moving all NULL to the bottom, what can I reference to join the two tables?
Previous output using one SELECT statement with 2 CASE statements
dinero mula
2.12688e+007 NULL
1.80031e+007 NULL
1.92415e+006 NULL
… …
NULL 93530.7
NULL 91000
NULL 84500
Desired output using one SELECT statement after creating two temp TABLES
dinero mula
2.12688e+007 93530.7
1.80031e+007 91000
1.92415e+006 84500
… 82500
NULL 82000
NULL …
NULL NULL
This is Microsoft SQL Server 2008. I'm super new to this, so please give an answer as clear and simplified as possible. Thank you.
Select A.dinero, B.mula
FROM #100plus as A FULL OUTER JOIN #20plus as B
ON A.dinero = B.mula
ORDER BY A.dinero,B.mula;
If you divide your table into two and use row_number() to generate numbers by descending order, you can connect them by row_number. Note how numbers are produced - row_number is given order by.
; WITH _20plus AS (
SELECT CASE WHEN income >= 20000 AND income < 100000
THEN income
END AS Mula,
Row_number() OVER (ORDER BY CASE WHEN income >= 20000 AND income
< 100000 THEN income END DESC) rn
FROM earners
),
_100plus AS (
SELECT CASE WHEN income >= 100000 THEN income
END AS Dinero,
Row_number() OVER (ORDER BY CASE WHEN income >= 100000
THEN income END DESC) rn
FROM earners
)
SELECT a.dinero,
b.mula
FROM _100plus AS a
INNER JOIN _20plus AS b
ON a.rn = b.rn