SELECT DISTINCT
F_Emp_code,CONVERT(varchar, t.F_Log_dtPunched, 103) AS F_Tbl_dtpunched,
Loc.F_Loc_code,COUNT(TF.F_Ter_LocCode)'Worked'
FROM
(select *, row_number() over (partition by F_Emp_code order by F_Emp_code) as rn from T_Mst_Employee ) A
LEFT JOIN T_Mst_Company co ON co.F_Com_code = A.F_Emp_Company_ID
RIGHT JOIN T_Tra_Loginfo t ON t.F_Log_Emp_Code = F_Emp_code
JOIN dbo.T_Mst_terminalinfo TF ON TF.F_Ter_nTerminalID=t.F_Log_TerminalID
LEFT JOIN T_Mst_Location Loc ON Loc.F_Loc_code = TF.F_Ter_LocCode
WHERE A.F_Emp_Status = 'Active'
AND F_Emp_code='100229'
and t.F_Log_dtPunched between '2017-01-01' and '2017-01-08'
group by co.F_Com_Logo, A.f_emp_firstname,A.F_Emp_MiddleName,
A.F_Emp_LastName,F_Emp_code,
F_Log_dtPunched,co.F_Com_Desc,Loc.F_Loc_code,Loc.F_Loc_Desc,TF.F_Ter_nTerminalID
My result is like this :
empid date loccode worktime
100229 01/01/2017 05138 1
100229 02/01/2017 05138 1
100229 03/01/2017 05138 1
100229 05/01/2017 09409 1
100229 06/01/2017 05138 1
100229 07/01/2017 01305 1
100229 07/01/2017 05138 1
if any employee worked same date with differnt location then i want to show result in worktime= 1/count of different location
example if employee worked 3 location in same day ,,so i need to calculate worktime 1/3 in working time column
You are looking for COUNT(DISTINCT column) OVER (...):
worktime * 1.0 / count(distinct loccode) over (partition by emp_id, date)
The multiplication with 1.0 is necessary in order to avoid integer division.
Related
I need to get member personal data for all our members whose subscriptions have lapsed i.e. have a subscription end date before 31/03/2020, however I want to show one member record only (distinct by membership number) ideally the most recent one
I've tried a ROW_NUMBER() solution SQL - Distinct One Col, Select Multiple other? and a cross apply solution sql distinct, getting 2 columns but I can't get it to work.
SELECT membershipnumber AS Id,
subscription.enddate
FROM [dbo].[userprofile]
INNER JOIN dbo.subscription
ON userprofile.id = subscription.userprofileid
INNER JOIN dbo.subscriptiontype
ON subscriptiontype.id = subscription.subscriptiontypeid
Output is
Id Enddate
1 2006-04-01 00:00:00.000
1 2001-04-01 00:00:00.000
1 1999-04-01 00:00:00.000
1 1998-04-01 00:00:00.000
1 2008-04-01 00:00:00.000
1 2007-04-01 00:00:00.000
1 2011-04-01 00:00:00.000
1 2005-04-01 00:00:00.000
1 2000-04-01 00:00:00.000
1 1997-04-01 00:00:00.000
2 1999-04-01 00:00:00.000
2 2012-04-01 00:00:00.000
2 2004-04-01 00:00:00.000
2 2001-04-01 00:00:00.000
2 2018-04-01 00:00:00.000
2 2009-04-01 00:00:00.000
2 2005-04-01 00:00:00.000
2 1997-04-01 00:00:00.000
Desired output
Id Enddate
1 2011-04-01 00:00:00.000
2 2018-04-01 00:00:00.000
Solved sql answer
;WITH cte
AS (SELECT membershipnumber AS Id,
subscription.enddate,
Row_number()
OVER (
partition BY membershipnumber
ORDER BY subscription.enddate DESC) AS rownumber
FROM [dbo].[userprofile]
INNER JOIN dbo.subscription
ON userprofile.id = subscription.userprofileid
INNER JOIN dbo.subscriptiontype
ON subscriptiontype.id = subscription.subscriptiontypeid
)
SELECT *
FROM cte
WHERE rownumber = 1
https://stackoverflow.com/a/6841644/5859743
Not sure if I got your question right.
but you can use DISTINCT in the SELECT, that would show only one record for each member.
SELECT DISTINCT Membershipnumber as Id
,'P' as PartyType
,'A' as Status
,case
when Name = 'Standard Membership paid annually.' and EndDate > '2020-03-31' then 'Member'
when Name = 'Lapsed subscription renewal' and EndDate > '2020-03-31' then 'Member'
when Name = '3 Year Subscription (members outside of UK and Ireland, Jersey, Guernsey and the Channel Islands)' and EndDate > '2020-03-31' then 'Overseas member'
when Name = '1 Year Subscription (members outside of UK and Ireland, Jersey, Guernsey and the Channel Islands).' and EndDate > '2020-03-31' then 'Overseas member'
when Name = 'Lapsed subscription renewal' and EndDate > '2020-03-31' then 'Member'
when Name = 'Lifetime membership' then 'Lifetime member'
when Name = 'Retired membership paid annually' and EndDate > '2020-03-31' then 'Retired member'
else 'Non member'
end As MemberType
,Title as NamePrefix
,FirstName as FirstName
,Surname as LastName
,DateOfBirth as BirthDate
,'Home' as AddressPurpose
,'Default' as CommunicationReasons
,AddressLine1
,AddressLine2
,AddressLine3
,Addressline4 as CityName
,'' as CountrySubEntityName
,Country as CountryCode
,'' as CountryName
,Postcode as PostalCode
,EmailAddress as Email
FROM [dbo].[UserProfile]
inner join dbo.Subscription on
UserProfile.Id = Subscription.UserProfileId
inner join dbo.SubscriptionType on
SubscriptionType.id = Subscription.SubscriptionTypeId```
If you are getting as above mentioned output. Then from that, your desired output will easily get using distinct.
; with cte as (
----- query which gives you above mentioned output
)
select distinct id, max(Enddate) as Enddate from cte
I suspect you want something like this:
select *
from (select . . ., -- all the columns you want
row_number() over (partition by Membershipnumber as Id order by s.Enddate) as seqnum
from [dbo].[UserProfile] up inner join
dbo.Subscription s
on up.Id = s.UserProfileId inner join
dbo.SubscriptionType st
on st.id = s.SubscriptionTypeId
) x
where seqnum = 1;
I need to create a report and I am struggling with the SQL script.
The table I want to query is a company_status_history table which has entries like the following (the ones that I can't figure out)
Table company_status_history
Columns:
| id | company_id | status_id | effective_date |
Data:
| 1 | 10 | 1 | 2016-12-30 00:00:00.000 |
| 2 | 10 | 5 | 2017-02-04 00:00:00.000 |
| 3 | 11 | 5 | 2017-06-05 00:00:00.000 |
| 4 | 11 | 1 | 2018-04-30 00:00:00.000 |
I want to answer to the question "Get all companies that have been at least for some point in status 1 inside the time period 01/01/2017 - 31/12/2017"
Above are the cases that I don't know how to handle since I need to add some logic of type :
"If this row is status 1 and it's date is before the date range check the next row if it has a date inside the date range."
"If this row is status 1 and it's date is after the date range check the row before if it has a date inside the date range."
I think this can be handled as a gaps and islands problem. Consider the following input data: (same as sample data of OP plus two additional rows)
id company_id status_id effective_date
-------------------------------------------
1 10 1 2016-12-15
2 10 1 2016-12-30
3 10 5 2017-02-04
4 10 4 2017-02-08
5 11 5 2017-06-05
6 11 1 2018-04-30
You can use the following query:
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
ORDER BY company_id, effective_date
to get:
id company_id status_id effective_date grp
-----------------------------------------------
1 10 1 2016-12-15 0
2 10 1 2016-12-30 1
3 10 5 2017-02-04 2
4 10 4 2017-02-08 2
5 11 5 2017-06-05 0
6 11 1 2018-04-30 0
Now you can identify status = 1 islands using:
;WITH CTE AS
(
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
)
SELECT id, company_id, status_id, effective_date,
ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) -
cnt AS grp
FROM CTE
Output:
id company_id status_id effective_date grp
-----------------------------------------------
1 10 1 2016-12-15 1
2 10 1 2016-12-30 1
3 10 5 2017-02-04 1
4 10 4 2017-02-08 2
5 11 5 2017-06-05 1
6 11 1 2018-04-30 2
Calculated field grp will help us identify those islands:
;WITH CTE AS
(
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
), CTE2 AS
(
SELECT id, company_id, status_id, effective_date,
ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) -
cnt AS grp
FROM CTE
)
SELECT company_id,
MIN(effective_date) AS start_date,
CASE
WHEN COUNT(*) > 1 THEN DATEADD(DAY, -1, MAX(effective_date))
ELSE MIN(effective_date)
END AS end_date
FROM CTE2
GROUP BY company_id, grp
HAVING COUNT(CASE WHEN status_id = 1 THEN 1 END) > 0
Output:
company_id start_date end_date
-----------------------------------
10 2016-12-15 2017-02-03
11 2018-04-30 2018-04-30
All you want know is those records from above that overlap with the specified interval.
Demo here with somewhat more complicated use case.
Maybe this is what you are looking for? For these kind of questions, you need to join two instance of your table, in this case I am just joining with next record by Id, which probably is not totally correct. To do it better, you can create a new Id using a windowed function like row_number, ordering the table by your requirement criteria
If this row is status 1 and it's date is before the date range check
the next row if it has a date inside the date range
declare #range_st date = '2017-01-01'
declare #range_en date = '2017-12-31'
select
case
when csh1.status_id=1 and csh1.effective_date<#range_st
then
case
when csh2.effective_date between #range_st and #range_en then true
else false
end
else NULL
end
from company_status_history csh1
left join company_status_history csh2
on csh1.id=csh2.id+1
Implementing second criteria:
"If this row is status 1 and it's date is after the date range check
the row before if it has a date inside the date range."
declare #range_st date = '2017-01-01'
declare #range_en date = '2017-12-31'
select
case
when csh1.status_id=1 and csh1.effective_date<#range_st
then
case
when csh2.effective_date between #range_st and #range_en then true
else false
end
when csh1.status_id=1 and csh1.effective_date>#range_en
then
case
when csh3.effective_date between #range_st and #range_en then true
else false
end
else null -- ¿?
end
from company_status_history csh1
left join company_status_history csh2
on csh1.id=csh2.id+1
left join company_status_history csh3
on csh1.id=csh3.id-1
I would suggest the use of a cte and the window functions ROW_NUMBER. With this you can find the desired records. An example:
DECLARE #t TABLE(
id INT
,company_id INT
,status_id INT
,effective_date DATETIME
)
INSERT INTO #t VALUES
(1, 10, 1, '2016-12-30 00:00:00.000')
,(2, 10, 5, '2017-02-04 00:00:00.000')
,(3, 11, 5, '2017-06-05 00:00:00.000')
,(4, 11, 1, '2018-04-30 00:00:00.000')
DECLARE #StartDate DATETIME = '2017-01-01';
DECLARE #EndDate DATETIME = '2017-12-31';
WITH cte AS(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) AS rn
FROM #t
),
cteLeadLag AS(
SELECT c.*, ISNULL(c2.effective_date, c.effective_date) LagEffective, ISNULL(c3.effective_date, c.effective_date)LeadEffective
FROM cte c
LEFT JOIN cte c2 ON c2.company_id = c.company_id AND c2.rn = c.rn-1
LEFT JOIN cte c3 ON c3.company_id = c.company_id AND c3.rn = c.rn+1
)
SELECT 'Included' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date BETWEEN #StartDate AND #EndDate
UNION ALL
SELECT 'Following' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date > #EndDate
AND LagEffective BETWEEN #StartDate AND #EndDate
UNION ALL
SELECT 'Trailing' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date < #EndDate
AND LeadEffective BETWEEN #StartDate AND #EndDate
I first select all records with their leading and lagging Dates and then I perform your checks on the inclusion in the desired timespan.
Try with this, self-explanatory. Responds to this part of your question:
I want to answer to the question "Get all companies that have been at
least for some point in status 1 inside the time period 01/01/2017 -
31/12/2017"
Case that you want to find those id's that have been in any moment in status 1 and have records in the period requested:
SELECT *
FROM company_status_history
WHERE id IN
( SELECT Id
FROM company_status_history
WHERE status_id=1 )
AND effective_date BETWEEN '2017-01-01' AND '2017-12-31'
Case that you want to find id's in status 1 and inside the period:
SELECT *
FROM company_status_history
WHERE status_id=1
AND effective_date BETWEEN '2017-01-01' AND '2017-12-31'
First of all I would like to thank the friends who helped this complex and difficult query.
I have three tables
Table 1
StaffId FirstName LastName staffType
---------------------------------------
1 Adam Sorme Student
2 Lara Sandra Teacher
3 Jack Jones Student
Table 2
GateId GateName
---------------------------------------
1 frontDoor
2 superDoor
Table 3
Id transitionDate GateId StaffId
---------------------------------------
1 2018-01-1 08:00:00 1 1
2 2018-01-1 10:00:00 2 1
3 2018-01-1 20:00:00 2 1
4 2018-01-2 07:00:00 1 2
5 2018-01-2 10:00:00 1 3
6 2018-01-9 12:00:00 2 2
I want the first and last movements of students for each day. Value must be set to null if no movement is available between the specified dates
transitionDate> '2018-01-1 00:00:00 000'
and transitionDate< '2018-01-03 00:00:00 000'
OUTPUT:
Id Date MinTransitionDate MaxTransitionDate FirstGateName LastGateName StaffId StaffType
1 2018-01-01 2018-01-1 08:00:00 2018-01-1 20:00:00 frontDoor superDoor 1 Student
2 2018-01-01 null null null null 3 student
3 2018-01-02 null null null null 1 student
4 2018-01-02 2018-01-2 10:00:00 null frontDoor null 3 student
The following query is partially working.
select s.staffId, d.dte,
min(t.transitionDate) as first_change,
max(t.transitionDate) as first_change,
max(case when seqnum_asc = 1 then gateId end) as first_gateid,
max(case when seqnum_desc = 1 then gateId end) as last_gateid
from (select s.* from Staff s where stafftype = 'Student') s cross join
(select distinct cast(transitionDate as date) as dte from Transitions) d left join
(select t.*,
row_number() over (partition by StaffId, cast(transitionDate as date) order by transitionDate) as seqnum_asc,
row_number() over (partition by StaffId, cast(transitionDate as date) order by transitionDate desc) as seqnum_desc
from Transitions t
) t
on cast(t.transitiondate as date) = d.dte and
t.staffId = s.staffId and
1 in (t.seqnum_asc, t.seqnum_desc)
group by s.staffId, d.dte;
Here is a SQL Fiddle.
How do I add firstGateName and LastGateName to this query result?
You can just join your existing query to the Gates table to get those names, i.e.
<existing query>
inner join Gates g1 on g1.gateId = (required gate id)
In your case you can join using the aggregate value you've
select
q.*,
g1.GateName as first_gate_name,
g2.GateName as last_gate_name
from
-- use existing query as a subquery, so we can easily use the first/last_gateid values
(
select s.staffId, d.dte,
min(t.transitionDate) as first_change,
max(t.transitionDate) as last_change,
max(case when seqnum_asc = 1 then gateId end) as first_gateid,
max(case when seqnum_desc = 1 then gateId end) as last_gateid
from (select s.* from Staff s where stafftype = 'Student') s cross join
(select distinct cast(transitionDate as date) as dte from Transitions) d left join
(select t.*,
row_number() over (partition by StaffId, cast(transitionDate as date) order by transitionDate) as seqnum_asc,
row_number() over (partition by StaffId, cast(transitionDate as date) order by transitionDate desc) as seqnum_desc
from Transitions t
) t
on cast(t.transitiondate as date) = d.dte and
t.staffId = s.staffId and
1 in (t.seqnum_asc, t.seqnum_desc)
group by s.staffId, d.dte
) q
-- join on the appropriate gate ids
inner join Gates g1 on g1.gateId = q.first_gateid
inner join Gates g2 on g2.gateId = q.last_gateid
I have following table. How I can find out overlapping spans only? In example, below memberid 3 should not be in our scope since date spans do not overlap with each other
Any help is highly appreciated
MemberID fromdate todate
1 1/1/2018 12/31/2018
1 1/1/2018 12/31/2018
2 12/1/2017 1/1/2019
2 1/2/2018 2/2/2019
3 1/1/2015 12/31/2015
3 1/1/2016 12/31/2016
3 1/1/2017 12/31/2017
4 1/1/2018 1/1/2018
4 1/1/2018 1/1/2018
5 1/1/2015 1/31/2016
5 1/1/2016 7/31/2016
5 07/01/2016 12/31/2016
Expected results should be data associated with Member Ids 1,2,4 and 5 Member ID 3 should not be in the results set because date spans are not overlapping.
Hmmm. You can get the overlapping spans by doing:
select m.*
from members m
where exists (select 1
from members m2
where m2.memberid = m.memberid and
m2.todate > m.fromdate and m2.fromdate < m.todate
);
If you want members that don't overlap, let's use except:
select m.memberid
from members m
except
select m.*
from members m
where exists (select 1
from members m2
where m2.memberid = m.memberid and
m2.todate >= m.fromdate and m2.fromdate <= m.todate
);
Except removes duplicates. But if you wanted to be extra sure and redundant, you could write select distinct for each query.
Try this:
;with cte as
(select memberid, convert(Varchar,fromdate,101)fromdate,convert(Varchar,todate,101)todate from #tb),
cte2 as
(select Num,memberid,todate,fromdate,Num + 1 as num2 from
(select ROW_NUMBER() over(partition by memberid order by fromdate) as Num,memberid,fromdate,todate from cte) as a),
cte3 as
(select memberid,fromdate,todate, DATEDIFF(day,fromdate,todate) as date_diff from
(select ISNULL(memberid,bnum)memberid , isnull(fromdate1,fromdate2)fromdate,isnull(fromdate2,fromdate1)todate,bnum from
(select a.num,a.fromdate,a.todate,a.num2 as num1,a.memberid,case when a.Num=b.num2 then b.todate else a.fromdate end as fromdate1,
case when a.Num=b.num2 then a.fromdate else b.todate end as fromdate2,b.num2,b.todate as todate2,b.Num as bnum from cte2 as a
full join cte2 as b
on a.num = b.num2 and a.memberid = b.memberid) as a) as a)
select distinct memberid from cte3 where date_diff<0
I am making a payroll system and I bought the B3 tft from zktechnology and would like to arrange the record.
Currently I can pull the data from the biometric with this format:
Count EmpID InOutMode Date
1 1 0 8/20/2012 07:49:01
2 1 1 8/20/2012 12:08:21
3 1 0 8/20/2012 12:43:10
4 1 1 8/20/2012 17:56:15
5 2 0 8/20/2012 07:53:11
6 2 1 8/20/2012 12:02:01
7 2 0 8/20/2012 12:39:56
8 2 1 8/20/2012 17:20:43
9 1 0 8/21/2012 08:10:20
10 1 1 8/21/2012 12:01:26
11 1 0 8/21/2012 13:03:11
12 1 1 8/21/2012 17:11:15
13 2 0 8/21/2012 07:48:26
14 2 1 8/21/2012 12:14:58
15 2 0 8/21/2012 12:59:31
16 2 1 8/21/2012 17:20:12
InOutMode:
0 = In, 1 = Out
Now, I want to convert the data above like this:
EmpID Date AM_In AM_Out PM_In PM_Out
1 8/20/2012 07:49:01 12:08:21 12:43:10 17:56:15
2 8/20/2012 07:53:11 12:02:01 12:39:56 17:20:43
1 8/21/2012 08:10:20 12:01:26 13:03:11 17:11:15
2 8/21/2012 07:48:26 12:14:58 12:59:31 17:20:12
So I can save it to the database with EmpID, Date, AM_In, AM_Out, PM_In, PM_Out fields.
I saw a similar code here before but I can't remember the URL.
Update:
VB.net code or sql in ms access format is acceptable.
It might be easiest to create two cross tabs and a query to join them to the available dates and employees.
1 AM Crosstab
TRANSFORM Min(tm.Date) AS MinOfDate
SELECT Format([Date],"dd/mm/yyyy") AS dt, tm.EmpID
FROM tm
GROUP BY Format([Date],"dd/mm/yyyy"), tm.EmpID
PIVOT tm.InOutMode;
2 PM Crosstab
TRANSFORM Max(tm.Date) AS MinOfDate
SELECT Format([Date],"dd/mm/yyyy") AS dt, tm.EmpID
FROM tm
GROUP BY Format([Date],"dd/mm/yyyy"), tm.EmpID
PIVOT tm.InOutMode;
Where tm is the name of your table.
You can then join these up.
SELECT Alldates.dt,
Alldates.empid,
am.[0] AS [Am In],
am.[1] AS [Am Out],
pm.[0] AS [Pm In],
pm.[1] AS [Pm Out]
FROM ((SELECT DISTINCT Format([date], "dd/mm/yyyy") AS dt,
empid
FROM tm) AS Alldates
LEFT JOIN am
ON ( Alldates.empid = am.empid )
AND ( Alldates.dt = am.dt ))
LEFT JOIN pm
ON ( Alldates.empid = pm.empid )
AND ( Alldates.dt = pm.dt );
Here is a query that should produce the results that you want in MS Access:
select am_in.empid,
format(am_in.min_in_dt, "MM/DD/YYYY") as [date],
format(am_in.min_in_dt, "hh:mm:ss") as AM_In,
format(am_out.min_out_dt, "hh:mm:ss") as AM_Out,
format(pm_in.max_in_dt, "hh:mm:ss") as PM_In,
format(pm_out.max_out_dt, "hh:mm:ss") as PM_Out
from
(
(
(
SELECT empid,
min(dt) as min_in_dt
FROM yourTable
where inoutmode = 0
group by empid
) am_in
inner join
(
SELECT empid,
min(dt) as min_out_dt
FROM yourTable
where inoutmode = 1
group by empid
) am_out
on am_in.empid = am_out.empid
)
inner join
(
SELECT empid,
max(dt) as max_in_dt
FROM yourTable
where inoutmode = 0
group by empid
) pm_in
on am_in.empid = pm_in.empid
)
inner join
(
SELECT empid,
max(dt) as max_out_dt
FROM yourTable
where inoutmode = 1
group by empid
) pm_out
on am_in.empid = pm_out.empid
Something like this will work
select
empid,
dateadd(day,datediff(day,0,DATE),0) as date,
max(case when sno=1 then convert(varchar(8),DATE,108)) as AM_IN,
max(case when sno=2 then convert(varchar(8),DATE,108)) as AM_OUT,
max(case when sno=3 then convert(varchar(8),DATE,108)) as PM_IN,
max(case when sno=4 then convert(varchar(8),DATE,108)) as PM_OUT
from
(
select *,
row_number() over (partition by empid order by Empid) as sno
from
table
) as t
group by
empid,dateadd(day,datediff(day,0,DATE),0)