I have this record with time in/out for employee in multiple rows, how to query a pivot with same id, name but different time in/out?
Just like on image below:
You can use pivoting like:
;WITH dtrTable AS ( --just a test sample of your table
SELECT *
FROM (VALUES
(1, 'emp1', '2016-10-20', '2016-10-20 10:00:00.000', '2016-10-20 15:00:00.000'),
(1, 'emp1', '2016-10-20', '2016-10-20 15:30:00.000', '2016-10-20 17:00:00.000'),
(1, 'emp1', '2016-10-20', '2016-10-20 18:30:00.000', '2016-10-20 19:00:00.000'),
(2, 'emp2', '2016-10-20', '2016-10-20 10:00:00.000', '2016-10-20 19:00:00.000'),
(2, 'emp2', '2016-10-20', '2016-10-20 21:00:00.000', '2016-10-20 22:00:00.000'),
(2, 'emp2', '2016-10-21', '2016-10-20 11:00:00.000', '2016-10-20 21:00:00.000')
) as t(empId, empName, dtrDate, dtrIn, dtrOut)
)
SELECT *
FROM (
SELECT empId,
empName,
dtrDate,
[Columns]+seq as [Columns],
[Values]
FROM (
SELECT *,
CAST(ROW_NUMBER() OVER (PARTITION BY empId, dtrDate ORDER BY dtrIn) as nvarchar(100)) as seq
FROM dtrTable
) as t
UNPIVOT (
[Values] FOR [Columns] IN ([dtrIn],[dtrOut])
) as unpvt
) as d
PIVOT (
MAX([Values]) FOR [Columns] IN ([dtrIn1],[dtrOut1],[dtrIn2],[dtrOut2],[dtrIn3],[dtrOut3])
) as pvt
Output:
empId empName dtrDate dtrIn1 dtrOut1 dtrIn2 dtrOut2 dtrIn3 dtrOut3
1 emp1 2016-10-20 2016-10-20 10:00:00.000 2016-10-20 15:00:00.000 2016-10-20 15:30:00.000 2016-10-20 17:00:00.000 2016-10-20 18:30:00.000 2016-10-20 19:00:00.000
2 emp2 2016-10-20 2016-10-20 10:00:00.000 2016-10-20 19:00:00.000 2016-10-20 21:00:00.000 2016-10-20 22:00:00.000 NULL NULL
2 emp2 2016-10-21 2016-10-20 11:00:00.000 2016-10-20 21:00:00.000 NULL NULL NULL NULL
Unpivot part will give you this table:
empId empName dtrDate Columns Values
1 emp1 2016-10-20 dtrIn1 2016-10-20 10:00:00.000
1 emp1 2016-10-20 dtrOut1 2016-10-20 15:00:00.000
1 emp1 2016-10-20 dtrIn2 2016-10-20 15:30:00.000
1 emp1 2016-10-20 dtrOut2 2016-10-20 17:00:00.000
1 emp1 2016-10-20 dtrIn3 2016-10-20 18:30:00.000
1 emp1 2016-10-20 dtrOut3 2016-10-20 19:00:00.000
2 emp2 2016-10-20 dtrIn1 2016-10-20 10:00:00.000
2 emp2 2016-10-20 dtrOut1 2016-10-20 19:00:00.000
2 emp2 2016-10-20 dtrIn2 2016-10-20 21:00:00.000
2 emp2 2016-10-20 dtrOut2 2016-10-20 22:00:00.000
2 emp2 2016-10-21 dtrIn1 2016-10-20 11:00:00.000
2 emp2 2016-10-21 dtrOut1 2016-10-20 21:00:00.000
Here I use ROW_NUMBER() with partitioning to give some order to ins and outs. And also numbers, generated by ROW_NUMBER(), helps to create columns, we cannot use same column names in pivot part.
Related
There are monthly periods of customer segments, I need to get the periods of each segment for each client.
Dataset:
CREATE TABLE segm (
dt DATE,
client_id VARCHAR(6),
segment_id INT
)
INSERT INTO segm VALUES
('2018-01-31' ,'A11111', 2),
('2018-02-28' ,'A11111', 2),
('2018-03-31' ,'A11111', 1),
('2018-04-30' ,'A11111', 1),
('2017-11-30' ,'B22222', 1),
('2017-10-31' ,'B22222', 1),
('2017-09-30' ,'B22222', 3),
('2017-09-30' ,'C33333', 1),
('2017-10-31' ,'C33333', 1)
I do a SQL query:
SELECT
client_id
, segment_id
, FIRST_VALUE(dt) OVER(PARTITION BY dt ORDER BY dt) AS "first_value"
, LAST_VALUE(dt) OVER(PARTITION BY dt ORDER BY dt) AS "last_value"
FROM segm
ORDER BY client_id, segment_id, "first_value", "last_value";
I get the result:
client_id
segment_id
"first_value"
"last_value"
A11111
1
2018-03-31
2018-03-31
A11111
1
2018-04-30
2018-04-30
A11111
2
2018-01-31
2018-01-31
A11111
2
2018-02-28
2018-02-28
B22222
1
2017-10-31
2017-10-31
B22222
1
2017-11-30
2017-11-30
B22222
3
2017-09-30
2017-09-30
C33333
1
2017-09-30
2017-09-30
C33333
1
2017-10-31
2017-10-31
And I need:
client_id
segment_id
"first_value"
"last_value"
A11111
1
2018-03-31
2018-04-30
A11111
2
2018-01-31
2018-02-28
B22222
1
2017-10-31
2017-11-30
B22222
3
2017-09-30
2017-09-30
C33333
1
2017-09-30
2017-10-31
How can I fix the request to get the desired result?
Thanks.
I don't think you need analytic functions, simple aggregation will give you what you need:
select Client_Id, Segment_Id, Min(dt) firstValue, Max(dt) LastValue
from segm
group by client_id, segment_id
order by Client_Id, Segment_Id;
You can simplify your solution by using a MAX and MIN aggregation functions with a GROUP BY clause:
SELECT
client_id,
segment_id,
MIN(dt) AS first_value,
MAX(dt) AS last_value
FROM
segm
GROUP BY
client_id,
segment_id
ORDER BY
client_id,
segment_id,
first_value,
last_value
Lets say we have Three employees in Employee_Info table:
Emp_Name
Month
salary
emp1
2021-01
10000
emp1
2021-02
13000
emp1
2021-03
10000
emp2
2021-01
15000
emp2
2021-02
15000
emp2
2021-03
12000
emp3
2021-01
20000
emp3
2021-02
20000
emp3
2021-03
13000
I have to write a select sql query with an output like this:
Emp_Name
Month
salary
emp1
2021-01
10000
emp1
2021-02
13000
emp1
2021-03
10000
emp1
2021-03
33000
emp2
2021-01
15000
emp2
2021-02
15000
emp2
2021-03
12000
emp2
2021-03
42000
emp3
2021-01
20000
emp3
2021-02
20000
emp3
2021-03
13000
emp3
2021-03
53000
As you see the new row which should have the sum of all salaries for a particular employee.
Probably the simplest method is just to use union all:
select emp_name, month, salary
from t
union all
select emp_name, max(month), sum(salary)
from t
group by emp_name
order by emp_name, month, salary;
Note that if you want the cumulative salary on each row, then you can use window functions:
select t.*,
sum(salary) over (partition by emp_name order by month) as running_salary
from t;
Here is a db<>fiddle.
I have a table:
date id count(sub_id)
2016-03-01 2 1
2016-03-02 650 1
2016-03-03 2 1
2016-03-04 2697 2
2016-03-05 2 4
2016-03-06 2697 3
2016-03-07 1000 2
2016-03-08 2 3
2016-03-09 2697 3
the dates go from 1st march 2016 to 15 march 2016.
i need to check what id is present in every date - from 1 to 15 march.
i.e what user submitted data on every day
so far I have this
select submission_date, id, count(submission_id)
from submissions
group by submission_date, id
having count(submission_id) >= 1
but this only shows the dates and users and how many submissions they made on every day - for example 2.
You must group by id and check in the HAVING clause if the distinct number of dates of each id is equal to the distinct number of dates in the table:
select id
from submissions
group by id
having count(distinct submission_date) = (select count(distinct submission_date) from submissions)
If there are no duplicate id, submission_date combinations you can change to:
having count(submission_date) = (select count(distinct submission_date) from submissions)
and if also there are no null dates:
having count(*) = (select count(distinct submission_date) from submissions)
You should GROUP BY date
CREATE TABLE table1 (
`date` DATE,
`id` INTEGER
);
INSERT INTO table1
(`date`, `id`)
VALUES
('2016-03-01', '2'),
('2016-03-02', '650'),
('2016-03-03', '2'),
('2016-03-04', '2697'),
('2016-03-03', '2697'),
('2016-03-05', '2'),
('2016-03-06', '2697'),
('2016-03-07', '1000'),
('2016-03-08', '2'),
('2016-03-09', '2697');
SELECT `date`, GROUP_CONCAT(DISTINCT `id` ORDER BY `id`)
FROM table1
GROUP BY `date`
ORDER BY `date`
date | GROUP_CONCAT(DISTINCT `id` ORDER BY `id`)
:--------- | :----------------------------------------
2016-03-01 | 2
2016-03-02 | 650
2016-03-03 | 2,2697
2016-03-04 | 2697
2016-03-05 | 2
2016-03-06 | 2697
2016-03-07 | 1000
2016-03-08 | 2
2016-03-09 | 2697
db<>fiddle here
I would like know how to get the last value for each week.
Let's say I have the next values
-- Table 1 --
day value
2018-03-12 32
2018-02-14 42
2018-03-16 62
2018-03-19 82
2018-03-20 92
2018-03-21 102
2018-03-27 112
2018-03-28 122
2018-03-29 132
How can I get the next values which are the last values for each week. Assuming the week start on Monday.
Day Value
2018-03-16 62
2018-03-21 102
2018-03-29 132
I have everything settled here SQL Fiddle
You can get the week number of day then get the max value per week number.
select t1.*
from table1 t1
join (
select week(day) as wknum,
max(day) as day
from table1
group by week(day)
) t2
on t1.day=t2.day
Result:
day value
2018-03-16 62
2018-03-21 102
2018-03-29 132
You can group by YEARWEEK()
create table tbl (day date, value int);
✓
insert into tbl values
('2018-03-12', 32),
('2018-02-14', 42),
('2018-03-16', 62),
('2018-03-19', 82),
('2018-03-20', 92),
('2018-03-21', 102),
('2018-03-27', 112),
('2018-03-28', 122),
('2018-03-29', 132);
✓
select day, yearweek(day) from tbl;
day | yearweek(day)
:--------- | ------------:
2018-03-12 | 201810
2018-02-14 | 201806
2018-03-16 | 201810
2018-03-19 | 201811
2018-03-20 | 201811
2018-03-21 | 201811
2018-03-27 | 201812
2018-03-28 | 201812
2018-03-29 | 201812
select day, value
from tbl
join (select max(day) mday
from tbl
group by yearweek(day)) t
on day = mday
day | value
:--------- | ----:
2018-02-14 | 42
2018-03-16 | 62
2018-03-21 | 102
2018-03-29 | 132
dbfiddle here
This solution uses window functions and picks the latest date within the week.
https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html
I use SQL Server, but I believe this is the MySQL equivalent:
with cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY WEEKOFYEAR([day]) ORDER BY DAYOFWEEK([day]) DESC) AS counter_
from #table1
)
SELECT [day], [value]
FROM cte
WHERE counter_ = 1
Here's how you would do it in SQL Server - Use mysql equivalent
select b.day, b.value from (
select datepart(ww,day) a, max(day) b
from yourtable
group by datepart(ww,day))a
join yourtable b on a.a=datepart(ww,b.day) and a.b=b.day
Try this FIDDLE:
= Order by the closest to the end of every week
= Group by week
SELECT day, value
FROM (SELECT * FROM table1 ORDER BY DATEDIFF(day + INTERVAL 6 - weekday(day) DAY, day) ASC) t
GROUP BY week(day);
I'm new to sql servers, I have a table as follows
**Emp_name | year | rank **
emp1 2010 1
emp1 2011 2
emp1 2012 3
emp2 2012 1
emp2 2013 2
emp3 2009 3
emp2 2010 4
emp3 2011 5
Emp_name column has duplicate values, I want a query that will return following result,
**Emp_name | year | rank **
emp1 2010 1
null (emp1) 2011 2
null (emp1) 2012 3
emp2 2012 1
null (emp2) 2013 2
emp3 2009 3
null (emp2) 2010 4
null (emp3) 2011 5
Only 1 of the values from the duplicate values(Emp_name column should be seen and the rest as null or as a blank space.
try this:
CREATE TABLE Table1
(Emp_name varchar(4), year int, rank int)
;
INSERT INTO Table1
(Emp_name, year, rank)
VALUES
('emp1', 2010, 1),
('emp1', 2011, 2),
('emp1', 2012, 3),
('emp2', 2012, 1),
('emp2', 2013, 2),
('emp3', 2009, 3),
('emp2', 2010, 4),
('emp3', 2011, 5)
;
select
CASE WHEN rn=1 then EMP_NAME else null end EMP_NAME,
year,rank
from
(
select *,ROW_NUMBER() over (partition by emp_name order by (select 0)) as rn from Table1
) a