I have a table that show the relationship of each account and customers of each account. The relationship between account and customer may change over time. I want to show this change with a Merge Statement in a way that if even one of the relations of an account changes, then all the past data of that account gets IsCurrent=0 and new data gets IsCurrent=1. And if any of the relationships did not change then do nothing in the merge statement. By considering that I want to consider all of rows of an account for decide to change IsCurrent, StartDate, EndDate of destination table (as I show below). How can I do that?
Destination table:
AccountId
CustomerId
RelationId
StartDate
EndDate
IsCurrent
1234
12
1
2022-06-02
Null
1
1234
13
2
2022-06-02
Null
1
1234
14
5
2022-06-02
Null
1
Source Table:
AccountId
CustomerId
RelationId
Date
1234
12
1
2022-10-02
1234
14
6
2022-10-02
This is what I want to have:
AccountId
CustomerId
RelationId
StartDate
EndDate
IsCurrent
1234
12
1
2022-06-02
2022-10-01
0
1234
13
2
2022-06-02
2022-10-01
0
1234
14
5
2022-06-02
2022-10-01
0
1234
12
1
2022-10-02
NULL
1
1234
14
6
2022-10-02
NULL
1
I don't think merge can handle this kind of upsert, but you can do it in three steps:
Stop accounts that no longer exist
Stop accounts that have new date
Add new/changed accounts
Some SQL ideas...
--1.
UPDATE d
SET EndDate = DATEADD(DAY, -1, CAST(GETDATE() AS DATE))
, IsCurrent = 0
FROM Destination d
WHERE NOT EXISTS(
SELECT 1
FROM source s
WHERE s.accountId = d.AccountId
AND s.CustomerID = d.CustomerID
AND s.RelationId = d.RelationID
)
AND d.IsCurrent = 1
AND d.AccountId = 1234 -- Not sure about this one
I added a WHERE on accountID. Because let's say your Destination table also contained Accounts 1235, maybe they shouldn't be deactivated by missing account 1234, it's a bit unclear.
--2.
UPDATE d
SET EndDate = DATEADD(DAY, -1, s.Date)
, IsCurrent = 0
FROM Destination d
INNER JOIN source s
ON s.accountId = d.AccountId
AND s.CustomerID = d.CustomerID
AND s.RelationId = d.RelationID
AND s.Date > d.StartDate -- New start
WHERE d.IsCurrent = 1
--3.
INSERT INTO Destination (CustomerId, AccountID, RelationId, StartDate, Enddate,IsCurrent
)
SELECT CustomerId, AccountID, RelationId, StartDate, NULL Enddate,1 IsCurrent
FROM source s
WHERE NOT EXISTS(
SELECT 1
FROM Destination d
WHERE d.CustomerId = s.CustomerID
AND d.AccountID = s.AccountID
AND d.RelationID = s.RelationID
AND d.StartDate = s.Date
AND d.IsCurrent = 1
)
It's kinda busy though, so perhaps there's something easier, but your design is a bit complicated.
This stuff should be performed in transaction so you dont get half-baked operation
i hope someone can help me :)
I need a query to get user name | scorm module name | module start date | module completition date | (optional) total_time
My query for now is this but i'm not so good with postgresql :(
SELECT
u.firstname AS First,
u.lastname AS Last,
sc.name AS Modulo,
st.value AS Status,
'' AS Inizio,
TO_CHAR(TO_TIMESTAMP(st.timemodified), 'DD/MM/YYYY HH24:MI') AS Completamento
FROM prefix_scorm_scoes_track AS st
JOIN prefix_user AS u ON st.userid=u.id
JOIN prefix_user_info_data AS uid ON uid.userid = u.id
JOIN prefix_scorm AS sc ON sc.id=st.scormid
JOIN prefix_course AS c ON c.id=sc.course
JOIN prefix_groups AS g ON g.courseid = c.id
JOIN prefix_groups_members AS m ON g.id = m.groupid
WHERE (st.element='cmi.core.lesson_status' and st.value = 'completed') AND m.userid=u.id and c.id = %%COURSEID%%
UNION
SELECT
user2.firstname AS First,
user2.lastname AS Last,
'' AS Modulo,
'not_started' AS status,
TO_CHAR(TO_TIMESTAMP(st.value::int), 'DD/MM/YYYY HH24:MI') AS Inizio,
CAST(NULL AS text) AS Completamento
FROM prefix_user_enrolments AS ue
JOIN prefix_enrol AS e ON e.id = ue.enrolid
JOIN prefix_course AS c ON c.id = e.courseid
JOIN prefix_user AS user2 ON user2 .id = ue.userid
JOIN prefix_user_info_data AS uid ON uid.userid = user2.id
JOIN prefix_groups AS g ON g.courseid = c.id
JOIN prefix_groups_members AS m ON g.id = m.groupid
JOIN prefix_scorm AS sc ON sc.course=c.id
Left Join prefix_scorm_scoes_track AS st on st.scormid=sc.id AND st.userid=user2.id
WHERE st.element = 'x.start.time' AND m.userid=user2.id and c.id = %%COURSEID%%
ORDER BY Last, First
The output is this:
first last modulo status inizio completamento
name lastname module 1 completed 29/03/2021 16:57
name lastname module 1 not_started 29/03/2021 16:52
name lastname module 2 not_started 31/03/2021 11:37
name lastname module 2 completed 31/03/2021 11:41
name lastname module 3 not_started 31/03/2021 11:26
name lastname module 3 completed 31/03/2021 11:32
name lastname module 4 not_started 31/03/2021 11:52
name lastname module 4 completed 31/03/2021 12:10
So now my question is how can i combine the results to have "inizio" and "completamento" on the same row?
I need some helps to join the tables I have currently.
Leave, Overtime And Roster's Date, EmployeeID need to match
Note: ShiftDuration is set to default value = 8.25
Note: Leave and Overtime table will only have entries when an employee applies for leave and overtime.
Employee
EmplyeeeID | Username | Password | GivenName | FamilyName | TeamID | ContactNo | StaffType
------------------------------------------------------------------------------------------
123 123 abc John Snow 1 999 1
1234 1234 abcd Jack Waller 2 223 1
12345 12345 abcde Ali Saw 1 123 1
123456 123456 abcdef Peter Peter 2 223 1
1234567 1234567 abcdeg Bryan Peter 1 333 1
Roster
Duty_ID | EmployeeID | Date | ShiftType | ShiftDuration
--------------------------------------------------------------------
2 123 2018-05-05 1 8.25
4 1234 2018-05-04 1 8.25
5 12345 2018-05-05 1 8.25
7 123456 2018-05-04 1 8.25
8 1234567 2018-05-05 1 8.25
Overtime
OTID | EmployeeID | Date | OT_Duration | OT_Reason
------------------------------------------------------------
2 1234 2018-05-04 2 Cover Duty
Leave
LeaveID | EmployeeID | Date | Duration_Off | Reason
----------------------------------------------------------
3 123 2018-05-05 2 NIL
IdealTable (Via Query)
Date | EmployeeID | GivenName | FamilyName | TeamID | ShiftType | ShiftDuration | Duration_Off | OT_Duration | Total_Hours
---------------------------------------------------------------------------------------------------------------------------------
2018-05-05 123 John Snow 1 1 8.25 2 0 6.25
2018-05-04 1234 Jack Waller 1 1 8.25 0 2 10.25
2018-05-05 12345 Ali Saw 1 1 8.25 0 0 8.25
2018-05-04 123456 Peter Peter 1 1 8.25 0 0 8.25
2018-05-05 1234567 Bryan Peter 1 1 8.25 2 0 8.25
I have 4 tables, they are Employee, Leave, Overtime, Roster
Employee
-EmployeeID (PK)
-Username
-Password
-GivenName
-FamilyName
-TeamID
-ContactNo
-StaffType
Leave
-LeaveID (PK)
-EmployeeID (FK)
-Date
-Duration_Off
-Reason
Overtime
-OTID (PK)
-EmployeeID (FK)
-Date
-OT_Duation
-OT_Reason
Roster
-DutyID (PK)
-EmployeeID (FK)
-Date
-ShiftType
-Shift Duration (Default Value = 8.25)
What I am trying to do is join the data from this 4 tables using Query
Ideal Table
-Date (From Leave, Overtime and Roster Table)
-EmployeeID (Employee Table)
-GivenName (Employee Table)
-FamilyName (Employee Table)
-TeamID (Employee Table)
-ShiftType (Roster Table)
-ShiftDuration (Roster Table)
-Duration_Off (Leave Table)
-OT_Duration (Overtime Table)
-Total_Hours (Calculation from joint table [(ShiftDuration + OT_Duration) - Duration_Off]
My database diagram design Do ignore the TimeData table as I initially wanted to use the TimeData table to achieve the IdealTable
My current query
USE [SMRT Dashboard]
GO
;With Dates
AS
(
SELECT [Date] FROM dbo.Roster
UNION
SELECT [Date] FROM dbo.Leave
UNION
SELECT [Date] FROM dbo.Overtime
),
Work_Matrix
AS
(
SELECT EmployeeID,[Date],ShiftType,ShiftDuration,CAST(NULL AS Decimal(30,2)) AS Duration_Off,CAST(NULL AS Decimal(30,2)) AS OT_Duration
FROM dbo.Roster
UNION ALL
SELECT EmployeeID,[Date], NULL, NULL,Duration_Off
FROM dbo.Leave
UNION ALL
SELECT EmployeeID,[Date],NULL,NULL,NULL,OT_Duration
FROM dbo.Overtime
)
SELECT d.[Date],
e.EmployeeID,
e.GivenName,
e.FamilyName,
e.TeamID,
w.ShiftType,
w.ShiftDuration,
w.Duration_Off,
w.OT_Duration,
w.Total_Hours
FROM Dates d
INNER JOIN
(
SELECT EmployeeID,
[Date],
MAX(ShiftType) AS ShiftType,
SUM(ShiftDuration) AS ShiftDuration,
SUM(Duration_Off) AS Duration_Off,
SUM(OT_Duration) AS OT_Duration,
SUM(ShiftDuration) + SUM(OT_Duration) - SUM(Duration_Off) AS Total_Hours
FROM Work_Matrix
GROUP BY EmployeeID,
[Date]
)w
ON d.[Date] = w.[Date]
JOIN dbo.Employee e
ON e.EmployeeID = w.EmployeeID
Current Errors:
Msg 205, Level 16, State 1, Line 4
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
I have not tested this but you may now check this. Hope your problem will be solved.
SELECT
a.EmployeeID,
a.GivenName,
a.FamilyName,
a.TeamID,
d.ShiftType,
d.ShiftDuration,
b.Duration_Off,
c.OT_Duration,
b.Date,
(d.ShiftDuration + c.OT_Duration) - b.Duration_Off as Total_Hours
FROM Employee a
INNER JOIN Roster d ON a.EmployeeID = d.EmployeeID
LEFT JOIN Leave b ON a.EmployeeID = b.EmployeeID
LEFT JOIN Overtime c ON a.EmployeeID = c.EmployeeID
I assume Date is same from all three tables (Leave, Overtime and Roster Table)
Try this query :
select
l.Date,
o.Date,
r.Date,
e.EmployeeID,
e.GivenName,
e.FamilyName,
e.TeamID,
r.ShiftType,
r.ShiftDuration,
l.Duration_Off,
o.OT_Duration,
((r.ShiftDuration+o.OT_Duration)-l.Duration_Off) as Total_Hours
FROM Employee e
INNER JOIN Leave l ON e.EmployeeID = l.EmployeeID
INNER JOIN Overtime o ON e.EmployeeID = o.EmployeeID
INNER JOIN Roster r ON r.EmployeeID = r.EmployeeID;
Try this Query ! You will get the correct output .
SELECT
e.EmployeeID AS 'Emp ID',
e.GivenName AS 'Emp Name',
l.Date AS Date,
e.FamilyName AS 'Family Name',
e.TeamID AS 'Team ID',
r.ShiftType AS 'Shift Type',
r.ShiftDuration AS 'Shift Duration',
l.Duration_Off AS 'Duration Off',
o.OT_Duration AS 'OT Duration',
(r.ShiftDuration + o.OT_Duration) - l.Duration_Off as 'Total Hours'
FROM
Employee e,
Leave l,
Overtime o,
Roster r
WHERE
e.EmployeeID = l.EmployeeID
AND
e.EmployeeID = o.EmployeeID
AND
e.EmployeeID = r.EmployeeID
I think the issue here is that an employee may have leave, may have overtime, or neither. By using INNER JOIN, you're filtering down the set of employees to ones that have Leave, and on a Roster, and have Overtime.
I created a SqlFiddle with the data you present (see here) that should get you closer.
SELECT
COALESCE(r.Date, o.Date, l.Date) as Date,
e.EmployeeID,
e.GivenName,
e.FamilyName,
e.TeamID,
r.ShiftType,
r.ShiftDuration,
IFNULL(l.Duration_Off, 0) as Duration_Off,
IFNULL(o.OT_Duration, 0) as OT_Duration,
r.ShiftDuration + IFNULL(o.OT_Duration, 0) - IFNULL(l.Duration_Off, 0) as Total_Hours
FROM Employee e
INNER JOIN Roster r on
e.EmployeeID = r.EmployeeID
LEFT JOIN Overtime o on
e.EmployeeID = o.EmployeeID
LEFT JOIN `Leave` l on
e.EmployeeID = l.EmployeeID
There's a few cavaets that this query won't solve:
If an employee has multiple leave entries or multiple overtime entries, the person will be listed N times. You can potentially use a GROUP BY statement to deal with this
The sample data in your question doesn't reconcile correctly. For example, Employe 1234 has a shift on 5/4/2018 and OT on 5/5/2018 but gets 10.25 on 5/4/2018.
If you use TimeData, this becomes a lot easier (assuming one record per date per employee):
SELECT
COALESCE(r.Date, o.Date, l.Date) as Date,
e.EmployeeID,
e.GivenName,
e.FamilyName,
e.TeamID,
r.ShiftType,
IFNULL(r.ShiftDuration, 0) as ShiftDuration,
IFNULL(l.Duration_Off, 0) as Duration_Off,
IFNULL(o.OT_Duration, 0) as OT_Duration,
IFNULL(r.ShiftDuration, 0) + IFNULL(o.OT_Duration, 0) - IFNULL(l.Duration_Off, 0) as Total_Hours
from TimeData t
INNER JOIN Employee e on
t.EmployeeID = e.EmployeeID
LEFT JOIN Roster r on
t.Duty_ID = r.Duty_ID
LEFT JOIN Overtime o on
t.OTID = o.OTID
LEFT JOIN `Leave` l on
t.LeaveID = l.LeaveID
Example SqlFiddle can be found here
I have 2 tables, let says the first table named "Schedule" and the second named "Setting".
TABLE "SCHEDULE"
ID Name StartTime EndTime Room
---------------------------------------------
111 AAAA 08:00 09:00 -
112 BBBB 08:00 09:00 -
113 CCCC 08:00 09:00 -
114 DDDD 08:30 09:30 -
115 EEEE 08:30 09:30 -
116 FFFF 08:30 09:30 -
TABLE "SETTING"
ID Type Room
------------------------
111 1 BPD01
112 2 BPR33
113 2 BPR33
114 2 BPR35
115 2 BPR33
I want to get the result like:
Room StartTime EndTime Total Used
---------------------------------------
BPR33 08:00 09:00 2
BPR33 08:30 09:30 1
BPR35 08:30 09:30 1
I have the code like:
SELECT ST.Room, S.StartTime, S.EndTime, COUNT(*) AS [Total Used]
FROM Schedule AS S CROSS JOIN Setting AS ST
WHERE (ID IN
(SELECT ID FROM Setting
WHERE (Type = '2')))
GROUP BY ST.Room, S.StartTime, S.EndTime
But the code resulted in showing all of the record in Setting with the counted value, the filter does not run properly.
How to do that?
I see a few potential issues here:
Your WHERE ID IN (SELECT ID FROM Setting WHERE Type='2') looks like it could just be WHERE Type='2'
What data type is your Type column? If it is INT, do WHERE Type = 2 as opposed to WHERE Type = '2' (which you'd use if Type was VARCHAR).
You do not appear to have a JOIN condition linking your two tables together (e.g. WHERE S.ID = ST.ID)
All together now:
SELECT ST.Room, S.StartTime, S.EndTime, COUNT(*) AS [Total Used]
FROM Schedule AS S CROSS JOIN Setting AS ST
WHERE S.ID = ST.ID
AND Type = 2
GROUP BY ST.Room, S.StartTime, S.EndTime
SELECT ST.Room, S.StartTime, S.EndTime, COUNT(1) AS [Total Used]
FROM Setting AS ST
LEFT JOIN Schedule AS S
ON S.ID = ST.ID
WHERE ST.Type = '2'
GROUP BY ST.Room, S.StartTime, S.EndTime
This shows the ones that were not scheduled with a zero count. If you don't want that the join would change to:
SELECT ST.Room, S.StartTime, S.EndTime, COUNT(1) AS [Total Used]
FROM Setting AS ST
JOIN Schedule AS S
ON S.ID = ST.ID
WHERE ST.Type = '2'
GROUP BY ST.Room, S.StartTime, S.EndTime
SELECT ST.Room,Type AS [Total Used]
FROM Schedule AS S CROSS JOIN Setting AS ST
WHERE (ID IN
(SELECT ID FROM Setting
WHERE (Type = '2')))
GROUP BY ST.Room
I wasn't sure what could be the title for my question so sorry about that.
I'm trying to write a SQL query to achieve the no. of members who should get reimbursed from a pharmacy.
For example : I went to pharmacy, I took a vaccine but by mistake I paid from my pocket. so now Pharmacy needs to reimburse me that amount. Lets say I have the data like:
MemberId Name ServiceDate PresNumber PersonId ClaimId AdminFee(in $)
1 John 1/1/2011 123 345 456 0
1 John 1/21/2011 123 345 987 20
2 Mike 2/3/2011 234 567 342 0
2 Mike 2/25/2011 234 567 564 30
5 Linda 1/4/2011 432 543 575 0
5 Linda 4/6/2011 987 543 890 0
6 Sonia 2/6/2011 656 095 439 0
This data shows all members from that pharmacy who got reimbursed and who haven't.
I need to find out the member having AdminFee 0 but i also need to check another record for the same member having same PresNumber, same PersonId where the ServiceDate falls within 30 Days of the Original Record.
If another record meets this criteria and the AdminFee field contains a value (is NOT 0) then it means that person has already been reimbursed. So from the data you can see John and Mike have already been reimbursed and Linda and Sonia need to be reimbursed.
Can anybody help me how to write an SQL query on this?
You don't mention what SQL engine you're using, so here is some generic SQL. You'll need to adapt the date math and the return of True/False ( in the second option) to whatever engine you're using:
-- Already reimbursed
SELECT * FROM YourTable YT1 WHERE AdminFee = 0 AND EXISTS
(SELECT * FROM YourTable YT2
WHERE YT2.MemberID = YT1.MemberID AND
YT2.PresNumber = YT1.PresNumber AND
YT2.ServiceDate >= YT1.ServiceDate - 30 AND
AdminFee > 0)
-- Need reimbursement
SELECT * FROM YourTable YT1 WHERE AdminFee = 0 AND NOT EXISTS
(SELECT * FROM YourTable YT2
WHERE YT2.MemberID = YT1.MemberID AND
YT2.PresNumber = YT1.PresNumber AND
YT2.ServiceDate >= YT1.ServiceDate - 30 AND
AdminFee > 0)
or
-- Both in one.
SELECT YT1.*,
CASE WHEN YT2.MemberID IS NULL THEN False ELSE True END AS AlreadyReimbursed
FROM YourTable YT1 JOIN YourTable YT2 ON
YT1.MemberID = YT2.MemberID AND
YT1.PresNumber = YT2.PresNumber AND
YT1.ServiceDate <= YT2.ServiceDate + 30
WHERE YT1.AdminFee = 0 AND YT2.AdminFee > 0)
You need to use datediff function in SQL Server and as parameter to pass day and to join the table above by other alias. I do not have SQL Server but I think it should be like this
Select memberid
from PaymentLog p
inner join PaymentLog d on p.serviceid = d.serviceid
and p.memberid = d.memberid
and p.personid = d.personid
Where adminfee = 0
and datediff(day, p.servicedate, d.servicedate) < 30
I called a table paymentlog