SQL Consecutive Days - Oracle - sql

[Data]
[Emp] [Emp_group] [Date_purchase]
1 001 12-jan-2016
1 001 13-jan-2016
1 001 19-jan-2016
1 003 14-jan-2016
2 004 21-feb-2016
2 004 22-feb-2016
2 004 23-feb-2016
3 005 01-apr-2016
Need SQL to find consecutive purchase dates. Emp (1) of emp group (001) has purchased consecutively on 12 and 13 of January. Emp and Emp group partition must be considered.

Just use lag()/lead():
select t.*
from (select t.*,
lag(date_purchase) over (partition by emp, emp_group order by date_purchase) as prev_dp,
lead(date_purchase) over (partition by emp, emp_group order by date_purchase) as next_dp
from t
) t
where date_purchase in (prev_dp, next_dp);

Related

SQL Query to find the previous job , location of a particular job type

I have the below table with data -
I want to create a query that displays the employees that had previous job as student and were then converted to "Employee" even if the final status is ACTIVE/INACTIVE
emp_number emp_status eff_start eff_end job Location
99 ACTIVE 01-JAN-2020 03-MAR-2020 Student Toronto
99 ACTIVE 04-MAR-2020 15-AUG-2020 Student Vancouver
99 ACTIVE 16-AUG-2020 22-AUG-2020 Contractor Toronto
99 ACTIVE 23-AUG-2020 28-SEP-2020 Employee Toronto
99 INACTIVE 29-SEP-2020 31-DEC-4712 ex- EMP Toronto
10 ACTIVE 03-FEB-2021 06-AUG-2021 Part-Student India
10 ACTIVE 07-AUG-2021 28-MAY-2022 Part-Student Toronto
10 ACTIVE 29-MAY-2022 31-DEC-4712 Employee Toronto
12 ACTIVE 03-FEB-2021 06-AUG-2021 Student India
12 ACTIVE 07-AUG-2021 28-MAY-2022 Student Toronto
12 ACTIVE 29-MAY-2022 31-DEC-4712 Contractor Toronto
I want to create a query that displays the below employees -
emp_number Previos_Location Previous_job Current_eff_start
99 Vancouver Student 29-sep-2020
10 Toronto Part-Student 29-MAY-2022
The above output, should show if previous job has anyting like "Student" mentioned. The previous_location , job sould have the latest location and job from the time when the job was "student/part-student". and current_eff_start should have the latest effective date of the employee.
if the student changes its job to anything apart from "employee" like "contractor" then it should not be picked.
Try below, try to improve the code if you want:
SELECT E.emp_number, STD.Location AS Previos_Location, STD.job AS Previous_job, E.eff_start AS Current_eff_start
FROM
(
SELECT T.*
FROM
(
SELECT emp_number, eff_start, Location, job, RANK() OVER (PARTITION BY emp_number ORDER BY eff_end DESC) RNK
FROM Employee
WHERE job IN ('Employee', 'ex- EMP')
) T
WHERE T.RNK = 1
) E
INNER JOIN
(
SELECT T.*
FROM
(
SELECT emp_number, Location, job, RANK() OVER (PARTITION BY emp_number ORDER BY eff_end DESC) RNK
FROM Employee
WHERE job IN ('Student', 'Part-Student')
) T
WHERE T.RNK = 1
) STD ON E.emp_number = STD.emp_number
Fiddle
You can use row_number() over() to determine the "most recent row" (by using descending order on a date/time column) then limit the output to just those rows by filtering on the row number result e.g.
select
*
from (
select
*
, row_number() over(partition by emp_number order by eff_end DESC) as rn
from mytable
where job like '%student%'
) d
where rn = 1
For the condition that someone has had both a student job and non-student job:
select
*
from (
select
mytable.*
, row_number() over(partition by emp_number
, case when lower(job) like '%student%' then 1 else 2 end
order by eff_end DESC) as rn
, count(case when lower(job) like '%student%' then 1 end) over(partition by emp_number) as sjobs
, count(case when lower(job) NOT like '%student%'
and lower(job) NOT like '%contract%'
then 1 end) over(partition by emp_number) as ojobs
from mytable
) d
where rn = 1
and lower(job) like '%student%'
and sjobs > 0
and ojobs > 0
+------------+------------+-----------+-----------+--------------+-----------+----+-------+-------+
| EMP_NUMBER | EMP_STATUS | EFF_START | EFF_END | JOB | LOCATION | RN | SJOBS | OJOBS |
+------------+------------+-----------+-----------+--------------+-----------+----+-------+-------+
| 10 | ACTIVE | 07-AUG-21 | 28-MAY-22 | Part-Student | Toronto | 1 | 2 | 1 |
| 99 | ACTIVE | 04-MAR-20 | 15-AUG-20 | Student | Vancouver | 1 | 2 | 3 |
+------------+------------+-----------+-----------+--------------+-----------+----+-------+-------+
or if you wish to ensure there were no non-student jobs change the where clause to:
and sjobs > 0
and ojobs = 0 /* no non-student job */
see: db<>fiddle here

I'm having trouble with COUNT() values when joining tables SQL

I have two independent tables, tbl_timesheet and tbl_absence. tbl_timesheet will have a row every day that an employee logs into a system. tbl_absence is a single row for a unique instance of absence, where the employee isn't in work. Each table looks like:
tbl_timesheet:
Staff_ID DEPT LOG_DATE
001 IT 2020-09-01
002 HR 2020-09-01
003 SALES 2020-09-01
001 IT 2020-09-02
002 HR 2020-09-02
003 SALES 2020-09-02
001 IT 2020-09-03
002 HR 2020-09-03
003 SALES 2020-09-03
tbl_absence:
Staff_ID ABSENCE_DATE
001 2020-09-10
003 2020-09-15
003 2020-09-22
I want to join the two tables, where I can count the instances of absence. I've attempted to do this using the following script:
SELECT t.Staff_ID as ID, t.DEPT as Dept, COUNT(a.Staff_ID) as 'Instances'
FROM tbl_timesheet t
JOIN tbl.absence a
ON t.Staff_ID = a.Staff_ID
GROUP BY t.Staff_ID, t.DEPT
I'd expect the following:
ID Dept Instances
001 IT 1
003 SALES 2
However due to the join between the tables, I believe the Staff_ID is being duplicated because each appears multiple times in tbl_timesheet.
Any suggestions?
when you JOIN two tables before getting distinct values of Staff_Id and Dept it will multiply the counts of records. for example staff_id='003' 2 record from absence table multiply 3 records from timesheet and you will get 6 records of it.Therefore you can code as below.
SELECT
t.Staff_ID as ID,
t.DEPT as Dept,
-----------
COUNT(a.Staff_ID) as Instances
-----------
FROM tbl_absence a
JOIN (select distinct Staff_ID, DEPT FROM tbl_timesheet) t
ON t.Staff_ID = a.Staff_ID
GROUP BY t.Staff_ID, t.DEPT

Row_Number Sybase SQL Anywhere change on multiple condition

I have a selection that returns
EMP DOC DATE
1 78 01/01
1 96 02/01
1 96 02/01
1 105 07/01
2 4 04/01
2 7 04/01
3 45 07/01
3 45 07/01
3 67 09/01
And i want to add a row number (il'l use it as a primary id) but i want it to change always when the "EMP" changes, and also won't change when the doc is same as previous one like:
EMP DOC DATE ID
1 78 01/01 1
1 96 02/01 2
1 96 02/01 2
1 105 07/01 3
2 4 04/01 1
2 7 04/01 2
3 45 07/01 1
3 45 07/01 1
3 67 09/01 2
In SQL Server I could use LAG to compare previous DOC but I can't seem to find a way into SYBASE SQL Anywhere, I'm using ROW_NUMBER to partitions by the "EMP", but it's not what I need.
SELECT EMP, DOC, DATE, ROW_NUMBER() OVER (PARTITION BY EMP ORDER BY EMP, DOC, DATE) ID -- <== THIS WILL CHANGE THE ROW NUMBER ON SAME DOC ON SAME EMP, SO WOULD NOT WORK.
Anyone have a direction for this?
You sem to want dense_rank():
select
emp,
doc,
date,
dense_rank() over(partition by emp order by date) id
from mytable
This numbers rows within groups having the same emp, and increments only when date changes, without gaps.
if performance is not a issue in your case, you can try sth. like:
SELECT tx.EMP, tx.DOC, tx.DATE, y.ID
FROM table_xxx tx
join y on tx.EMP = y.EMP and tx.DOC = y.DOC
(SELECT EMP, DOC, ROW_NUMBER() OVER (PARTITION BY EMP ORDER BY DOC) ID
FROM(SELECT EMP, DOC FROM table_xxx GROUP BY EMP, DOC)x)y

Select Most Recent Entry in SQL

I'm trying to select the most recent non zero entry from my data set in SQL. Most examples of this are satisfied with returning only the date and the group by variables, but I would also like to return the relevant Value. For example:
ID Date Value
----------------------------
001 2014-10-01 32
001 2014-10-05 10
001 2014-10-17 0
002 2014-10-03 17
002 2014-10-20 60
003 2014-09-30 90
003 2014-10-10 7
004 2014-10-06 150
005 2014-10-17 0
005 2014-10-18 9
Using
SELECT ID, MAX(Date) AS MDate FROM Table WHERE Value > 0 GROUP BY ID
Returns:
ID Date
-------------------
001 2014-10-05
002 2014-10-20
003 2014-10-10
004 2014-10-06
005 2014-10-18
But whenever I try to include Value as one of the selected variables, SQLServer results in an error:
"Column 'Value' is invalid in the select list because it is not
contained in either an aggregate function or the GROUP BY clause."
My desired result would be:
ID Date Value
----------------------------
001 2014-10-05 10
002 2014-10-20 60
003 2014-10-10 7
004 2014-10-06 150
005 2014-10-18 9
One solution I have thought of would be to look up the results back in the original Table and return the Value that corresponds to the relevant ID & Date (I have already trimmed down and so I know these are unique), but this seems to me like a messy solution. Any help on this would be appreciated.
NOTE: I do not want to group by Value as this is the result I am trying to pull out in the end (i.e. for each ID, I want the most recent Value). Further Example:
ID Date Value
----------------------------
001 2014-10-05 10
001 2014-10-06 10
001 2014-10-10 10
001 2014-10-12 8
001 2014-10-18 0
Here, I only want the last non zero entry. (001, 2014-10-12, 8)
SELECT ID, MAX(Date) AS MDate, Value FROM Table WHERE Value > 0 GROUP BY ID, Value
Would return:
ID Date Value
----------------------------
001 2014-10-10 10
001 2014-10-12 8
This can also be done using a window function which is very ofter faster than a join on a grouped query:
select id, date, value
from (
select id,
date,
value,
row_number() over (partition by id order by date desc) as rn
from the_table
) t
where rn = 1
order by id;
Assuming you don't have repeated dates for the same ID in the table, this should work:
SELECT A.ID, A.Date, A.Value
FROM
T1 AS A
INNER JOIN (SELECT ID,MAX(Date) AS Date FROM T1 WHERE Value > 0 GROUP BY ID) AS B
ON A.ID = B.ID AND A.Date = B.Date
select a.id, a.date, a.value from Table1 a inner join (
select id, max(date) mydate from table1
where Value>0 group by ID) b on a.ID=b.ID and a.Date=b.mydate
Using Subqry,
SELECT ID, Date AS MDate, VALUE
FROM table t1
where date = (Select max(date)
from table t2
where Value >0
and t1.id = t2.id
)
Answers provided are perfectly adequate, but Using CTE:
;WITH cteTable
AS
(
SELECT
Table.ID [ID], MAX(Date) [MaxDate]
FROM
Table
WHERE
Table.Value > 0
GROUP BY
Table.ID
)
SELECT
cteTable.ID, cteTable.Date, Table.Value
FROM
Table INNER JOIN cteTable ON (Table.ID = cteTable.ID)

How to select first 2 rows using group's

I have:
Table1
ID date amt
-------------------
001 21/01/2012 1200
001 25/02/2012 1400
001 24/03/2012 1500
001 21/04/2012 1000
002 21/03/2012 1200
002 01/01/2012 0500
002 08/09/2012 1000
.....
I want to select the first two rows from each group of ID ordered by date DESC from Table1.
Query looks like this:
SELECT TOP 2 DATE, ID, AMT FROM TABLE1 GROUP BY ID, AMT --(NOT WORKING)
Expected output:
ID date amt
-------------------
001 21/01/2012 1200
001 25/02/2012 1400
002 21/03/2012 1200
002 01/01/2012 0500
.....
you can take advantage of using Common table Expression and Window Function
WITH recordList
AS
(
SELECT ID, DATE, Amt,
DENSE_RANK() OVER (PARTITION BY ID ORDER BY DATE ASC) rn
FROM tableName
)
SELECT ID, DATE, Amt
FROM recordList
WHERE rn <= 2
SQLFiddle Demo
based on your desired result above, you are ordering the date by ASCENDING.
Ok, You can either use DENSER_RANK() or ROW_NUMBER() but in my answer, I've used DENSE_RANK() because I'm thinking of the duplicates. Anyway, it's the choice of the OP to use ROW_NUMBER() instead of DENSE_RANK().
TSQL Ranking Functions