Filter Out a Specific result from a complicated SQL statement and clean up my SQL - sql-server-2008-r2

I am taking a combination of the following tables to determine, in and out time, total hours worked for the day, workcenter and associated pay rates. However, when the PunchType = '303' there is two rows, 1 for the initial workcenter as a '10' punch and then the '303' When the '303 punch exists I need to use that as the in punch instead. The below query and sample results show that I have narrowed my query down to show both the results of using the '10' punch (in) - '12' punch (out) and the '303' punch (in) if it exists to the '12' punch (out). In my final result I only want the '303' - '12' match if it exists or I will have duplicate records. All of this is being dumped in Report Builder 3.0 to calculate totals hours worked and dollars paid out per day ( no need for help there, just trying to give some context)
I have included the RowNumber in the final query results because I was thinking to use that to filter the results as needed. My thinking: if Count(RowNumber) = 2, return where RowNumber = '2', IF Count(RowNumber) = 1, return where RowNumber = '1', IF Count(RowNumber) = 4, return Where RowNumber = '3,4). In know the syntax in the phrasing here is wrong, but I am just using it to illustrate what I am trying to do. I am sure there is an easier way to write the query (though I am OK with if not) as long as I can figure out how to filter the results to only what I need. Any help is appreciated. Thanks!
Sample Data:
Timecard
| TimeCardID | StoreID | EmpID | CardDate
| PunchType | WorkCenter | BreakIndex |ShadowTimeCardForID
B6B839AD-D8DF-E611-A3E5-0019170149B6 | 32365 | 4171 |2017-01-21 07:54:00.500
| 303 | 4 |0 | 00000000-0000-0000-0000-000000000000
EmployeeRate
| EmployeeRateID | EmployeeID
| RateIndex | WorkCenter | OvertimeRate | RegularRate
| C3325A54-E7A9-E611-A16D-0019178089A7 | 27139B5C-7A74-E611-969E-3417EBD1A8D1
| 4 | 4 | 2250 |1500
Query:
DECLARE #datetime datetime = '2017-01-22 04:00:00.000'
SELECT
z.EmpID,
z.RegularRate,
z.OvertimeRate,
z.WorkCenter,
z.in_punch,
z.out_punch,
z.HoursWorked,
z.RowNumber
FROM
(SELECT
y.EmpID,
y.RegularRate,
y.OvertimeRate,
y.WorkCenter,
y.in_punch,
y.out_punch,
y.HoursWorked,
row_number() OVER(PARTITION BY EmpID ORDER BY EmpiD) AS RowNumber
FROM
(SELECT
f.EmpID,
f.RegularRate,
f.OvertimeRate,
f.WorkCenter,
f.in_punch,
f.out_punch,
f.HoursWorked
FROM
(SELECT
tc.EmpID,
er.RegularRate,
er.OvertimeRate,
tc.WorkCenter,
tc.in_punch,
tc.out_punch,
CONVERT(varchar(3),DATEDIFF(MINUTE,in_punch,out_punch)/60) + ':' +
RIGHT('0' + CONVERT(varchar(2),DATEDIFF(MINUTE,in_punch,out_punch)%60),2)
AS HoursWorked,
row_number() OVER(PARTITION BY tc.EmpID ORDER BY tc.EmpiD) AS RowNumber
FROM
(SELECT
e.EmpID,
e.WorkCenter,
e.CardDate AS in_punch,
e2.CardDate AS out_punch
FROM
(SELECT EmpID, CardDate, WorkCenter FROM TimeCard where PunchType = '10'
AND CardDate BETWEEN DATEADD(DAY, -1, #datetime) AND #datetime) e
INNER JOIN
(SELECT EmpID, CardDate, WorkCenter
FROM TimeCard where PunchType = '12' AND CardDate BETWEEN DATEADD(DAY, -1,
#datetime) AND #datetime) e2
ON
e.EmpID = e2.EmpID
) tc
INNER JOIN
[dbo].[Employee] em
ON tc.EmpID = em.EmpID
INNER JOIN
[dbo].[EmployeeRate] er
ON em.[EmployeeID] = er.[EmployeeID] AND tc.[Workcenter] = er.[WorkCenter]
WHERE tc.in_punch <= tc.out_punch
GROUP BY tc.EmpID,
er.RegularRate,
er.OvertimeRate,
tc.WorkCenter,
tc.in_punch,
tc.out_punch
) f
WHERE f.[RowNumber] <> '2'
UNION
SELECT
f.EmpID,
f.RegularRate,
f.OvertimeRate,
f.WorkCenter,
f.in_punch,
f.out_punch,
f.HoursWorked
FROM
(SELECT
tc.EmpID,
er.RegularRate,
er.OvertimeRate,
tc.WorkCenter,
tc.in_punch,
tc.out_punch,
CONVERT(varchar(3),DATEDIFF(MINUTE,in_punch,out_punch)/60) + ':' +
RIGHT('0' +
CONVERT(varchar(2),DATEDIFF(MINUTE,in_punch,out_punch)%60),2) AS
HoursWorked,
row_number() OVER(PARTITION BY tc.EmpID ORDER BY tc.EmpiD) AS RowNumber
FROM
(SELECT
e.EmpID,
e.WorkCenter,
e.CardDate AS in_punch,
e2.CardDate AS out_punch
FROM
(SELECT EmpID, CardDate, WorkCenter
FROM TimeCard where PunchType = '303' AND CardDate BETWEEN DATEADD(DAY, -1,
#datetime) AND #datetime) e
INNER JOIN
(SELECT EmpID, CardDate, WorkCenter
FROM TimeCard where PunchType = '12' AND CardDate BETWEEN DATEADD(DAY, -1,
#datetime) AND #datetime) e2
ON
e.EmpID = e2.EmpID
) tc
INNER JOIN
[dbo].[Employee] em
ON tc.EmpID = em.EmpID
INNER JOIN
[dbo].[EmployeeRate] er
ON em.[EmployeeID] = er.[EmployeeID] AND tc.[Workcenter] = er.[WorkCenter]
WHERE tc.in_punch <= tc.out_punch
GROUP BY tc.EmpID,
er.RegularRate,
er.OvertimeRate,
tc.WorkCenter,
tc.in_punch,
tc.out_punch
) f
WHERE f.[RowNumber] <> '2'
) y
) z
GROUP BY
z.EmpID,
z.RegularRate,
z.OvertimeRate,
z.WorkCenter,
z.in_punch,
z.out_punch,
z.HoursWorked,
z.RowNumber
ORDER BY COUNT(RowNumber)OVER(PARTITION BY EmpID)
Results:
EmpID,RegularRate,OvertimeRate,WorkCenter,in_punch,out_punch,HoursWorked,RowNumber
9267,1150,1725,9,2017-01-21 16:59:27.940,2017-01-22 01:16:16.200,8:17, 1
9438,550,825,3,2017-01-21 09:55:34.500,2017-01-21 15:37:51.770,5:42,1
9471,223,335,1,2017-01-21 10:32:08.060,2017-01-21 14:18:23.430,3:46,1
9471,223,335,1,2017-01-21 15:54:29.570,2017-01-21 23:00:00.000,7:06,2
4171,223,335,1,2017-01-21 07:54:00.490,2017-01-21 15:17:31.740,7:23,1
4171,1500,2250,4,2017-01-21 07:54:00.500,2017-01-21 15:17:31.740,7:23,2

Related

How to add a condition in select class, based on the result value of another select class

I need a Postgres query to get "A value", "A value date", "B value" and "B value date"
The B value and date should be the one which is between 95 to 100 days of "A value date"
I have the query to get "A value" and "A value date", don't know how to get the B value and date by using the result (A value)
select u.id,
(select activity
from Sol_pro
where user_id = u.id
and uni_par = 'weight_m'
order by created_at asc
limit 1) as A_value
from users u;
the B_value and B_date from the same sol_pro table,
95-100 days after the A_value date (if more mores are there between 95-100, I need only one(recent) value) Expected
Output: id = 112233, A_Value = "weight = 210.25", A_value_date = 12-12-2020, B_value = "weight = 220.25", B_value_date = 12-12-2020
Well without table definition I developed it from the output columns and your original query. Further I had to make up stuff for the data, but the following should be close enough for you to see the technique involved. It is actually a simple join operation, just that it is self-join on the table sol_pol. (I.e it is joined to itself). Notice comments indicated by --<<<
select distinct on (a.id)
a.id
, a.user_id --<<< assumption needed
, a.activity "A_value"
, a.created_at::date "A_date"
, b.activity "B_value"
, b.created_at::date
from sol_pro a
join sol_pro b
on ( b.user_id = a.user_id --<<< assumption
and b.uni_par = a.uni_par --<<< assumption
)
where a.id = 112233 --<<< given Orig query
and a.uni_par = 'weight_m' --<<< given Orig query, but not needed if id is PK
and b.created_at between a.created_at + interval '95 days' --<<< date range inclusive of 95-100
and a.created_at + interval '100 days'
order by a.id, b.created_at desc;
See here for example run. The example contains a column you will not have "belayer_note". This is just a note-to-self I sometimes use for initial testing.
Suppose that you have tables users and measures:
# create table users (id integer);
# create table measures (user_id integer, value decimal, created_at date);
They are filled with test data:
INSERT INTO users VALUES (1), (2), (3);
INSERT INTO measures VALUES (1, 100, '9/10/2020'), (1, 103, '9/15/2020'), (1, 104, '10/2/2020');
INSERT INTO measures VALUES (2, 200, '9/11/2020'), (2, 207, '9/21/2020'), (2, 204, '10/1/2020');
INSERT INTO measures VALUES (3, 300, '9/12/2020'), (3, 301, '10/1/2020'), (3, 318, '10/12/2020');
Query:
WITH M AS (
SELECT
A.user_id,
A.value AS A_value, A.created_at AS A_date,
B.value AS B_value, B.created_at AS B_date
FROM measures A
LEFT JOIN measures B ON
A.user_id = B.user_id AND
B.created_at >= A.created_at + INTERVAL '5 days' AND
B.created_at <= A.created_at + INTERVAL '10 days'
ORDER BY
user_id, A_date, B_date
)
SELECT DISTINCT ON (user_id) * FROM M;
will select for each user:
the first available measurement (A)
the next measurement (B) which is made between 5-10 days from (A).
Result:
user_id | a_value | a_date | b_value | b_date
---------+---------+------------+---------+------------
1 | 100 | 2020-09-10 | 103 | 2020-09-15
2 | 200 | 2020-09-11 | 207 | 2020-09-21
3 | 300 | 2020-09-12 | [NULL] | [NULL]
(3 rows)
P.S. You must sort table rows carefully with ODRDER BY when using DISTINCT ON () clause because PostgreSQL will keep only first records and discard others.

Optimising T-SQL reporting performance

I have the table bellow, I need to delete opposite rows between two dates by pairs based on PerCode Value,
In fact, we delete rows inside the date range that have the same PerCode and have equal and opposite values.
The problem is that begin date and end date are provided by users as parameters while reporting but the query take too much time if i try to delete these at runtime.
Example:
Begin date = 01/01/2018
End date = 31/12/2018
I should delete rows 3 and 4.
Do u have any idea how to do that while optimising performance (the table have 200 Millions of rows)
+----+------------+---------+---------+-----------+
| Id | Date | PerCode | Value | IsDeleted |
+----+------------+---------+---------+-----------+
| 1 | 01/10/2017 | C1 | 10 | |
| 2 | 01/01/2018 | C1 | -10 | |
| 3 | 15/02/2018 | C2 | 20 | 1 |
| 4 | 10/03/2018 | C2 | -20 | 1 |
| 5 | 01/12/2018 | C3 | 15 | |
| 6 | 01/02/2019 | C3 | -15 | |
+----+------------+---------+---------------------+
I had a quick go at this, using a table variable to allow me to knock together a query using your test data. However, this might not perform well when used over 2 million rows?
DECLARE #table TABLE (id INT, [date] DATE, percode CHAR(2), [value] INT, isdeleted BIT);
INSERT INTO #table
SELECT 1, '20171001', 'C1', 10, NULL
UNION ALL
SELECT 2, '20180101', 'C1', -10, NULL
UNION ALL
SELECT 3, '20180215', 'C2', 20, NULL
UNION ALL
SELECT 4, '20180310', 'C2', -20, NULL
UNION ALL
SELECT 5, '20181201', 'C3', 15, NULL
UNION ALL
SELECT 6, '20190201', 'C3', -15, NULL;
DECLARE #date_from DATE = '20180101';
DECLARE #date_to DATE = '20181231';
WITH ordered AS (
SELECT
id,
percode,
[value],
ROW_NUMBER() OVER (PARTITION BY percode, [value] ORDER BY [value]) AS order_id
FROM
#table
WHERE
[date] BETWEEN #date_from AND #date_to
AND ISNULL(isdeleted, 0) != 1),
matches AS (
SELECT
m1.id AS match_1_id,
m2.id AS match_2_id
FROM
ordered m1
INNER JOIN ordered m2 ON m1.percode = m2.percode AND m1.[value] = m2.[value] * -1 AND m1.order_id = m2.order_id)
UPDATE
t
SET
isdeleted = 1
FROM
#table t
INNER JOIN matches m ON m.match_1_id = t.id OR m.match_2_id = t.id;
SELECT * FROM #table;
Results:
id date percode value isdeleted
1 2017-10-01 C1 10 NULL
2 2018-01-01 C1 -10 NULL
3 2018-02-15 C2 20 1
4 2018-03-10 C2 -20 1
5 2018-12-01 C3 15 NULL
6 2019-02-01 C3 -15 NULL
How does it work? Well I broke the task down into steps:
make a list of all rows in the date period specified, where they aren't already deleted;
for each row of data assign it a running count number, grouped by the percode and the value. So the first C1 10 would be number #1, then the second C1 10 would be number #2, etc.;
to find matches it's simply a case of finding any value that has the same percode, the equal and opposite value to another value group, and the same running count number;
where there's a match set the isdeleted flag to 1.
Here is my code but this is not performant over 200 millions rows in real time.
and in real life Percode is concatenation of 5 columns (date, varchar(13), varchar(2),varchar(1) and varchar(50)) and Value is 4 numeric columns.
I am searching for other ideas.
--DECLARE #table TABLE (id INT, [date] DATE, percode CHAR(2), [value] INT, isdeleted BIT);
Select * INTO #MasterTable FROM
(
SELECT 1 id, '20171001' [date], 'C1' percode, 10 [value], NULL isdeleted
UNION ALL
SELECT 2, '20180101', 'C1', -10, NULL
UNION ALL
SELECT 3, '20180215', 'C2', 20, NULL
UNION ALL
SELECT 4, '20180310', 'C2', -20, NULL
UNION ALL
SELECT 5, '20181201', 'C3', 15, NULL
UNION ALL
SELECT 6, '20190201', 'C3', -15, NULL
) T ;
DECLARE #date_from DATE = '20180101';
DECLARE #date_to DATE = '20181231';
select F.id
Into #TmpTable
from
(
select Id, PerCode, Value
,ROW_NUMBER() over (partition by PerCode, Value order by (select 0)) Rn2
from
#MasterTable ) F
inner join (
select
PerCode
, Rn1
from (
select
PerCode
,Value
,ROW_NUMBER() over (partition by PerCode, Value order by (select 0)) Rn1
FROM #MasterTable
where
[date] BETWEEN #date_from AND #date_to
) A
group by PerCode , Rn1
having sum(Value) = 0 and count(*)>1
) B on F.PerCode = B.PerCode
and F.Rn2 = B.Rn1
update R
set IsDeleted = 1
from #MasterTable R
inner join #TmpTable P
on R.id = P.id
select * from #MasterTable
drop table #MasterTable ;
drop table #TmpTable;

How to data show Data Row Column in Postgresql?

Here is my data and query. I want row data show in columns... Can someone help me to modify the query? I am using PostgreSQL queries.
select
ss.name, ip.product_name, ssr.quantity
from
services_servicerecipe ssr
inner join
services_service ss on ssr.service_id = ss.id
inner join
inventory_product ip on ssr.product_id = ip.id
order by
ss.name
Output:
Service_name | Product_name | Quantity
-------------+------------------+-----------
Balayage | 7.3-revlon | 2
Balayage | 701-revlon | 1
I want it to look like this
Service_name | Product_name | Quantity | Product_name | Quantity
-------------+-------------------+--------------+------------------+----------
Balayage | 7.3-revlon | 2 | 701-revlon | 1
Here is a pivot option, using ROW_NUMBER:
with cte as (
select ss.name, ip.product_name, ssr.quantity,
row_number() over (partition by ss.name order by ip.product_name) rn
from services_servicerecipe ssr
inner join services_service ss on ssr.service_id = ss.id
inner join inventory_product ip on ssr.product_id = ip.id
)
select
name,
max(case when rn = 1 then product_name end) as product1,
max(case when rn = 1 then quantity end) as quantity1,
max(case when rn = 2 then product_name end) as product2,
max(case when rn = 2 then quantity end) as quantity2
from cte
group by name;

how can i calculate sum of PaidAmount that is made between two dates

I have 3 tables which are Accounts, Payments, Statements. Table Accounts have all the accounts, table Payments have all the payments made to the account, and table Statements have all the statement data for the accounts.
Accounts
AccountID | DateOfDeath |
1001 | 2014-03-10 |
Payments
AccountID | PaidAmount | PaymentDate
1001 | 80.27 | 2014-07-09
1001 | 80.27 | 2014-06-10
1001 | 80.27 | 2014-05-12
1001 | 80.27 | 2014-04-13
1001 | 80.27 | 2014-03-15
1001 | 80.27 | 2014-02-14
Statements
AccountID | Balance | StatementDate
1001 | 0.00 | 2014-03-28
1001 | 1909.31 | 2014-02-25
I need to know the sum of PaidAmount (table Payments) in Payments table which is between the StatementDate (table Statements) of 2014-03-28 and 2014-02-25. The sum of the PaidAmount should have been 80.27 but I am getting 321.08. Can anyone tell me what I am doing wrong or how can I write the query in a better way?
here is what I have so far
create table #temp1
(
AccountID Numeric(9, 0)
, DateOfDeath date
, StatementDate date
, Balance numeric(17,2)
)
insert into #temp1
(
AccountID, DateOfDeath, StatementDate, Balance
)
select a.AccountID
,DateofDeath
,StatementDate
,Balance
from Accounts a
inner join Statements b on a.accountID = b.accountID
where StatementDate in (select top 1 statementdate
from Statements
where AccountID = a.AccountID
and StatementDate >= DateOfDeath
order by StatementDate)
Order By a.AccountID, StatementDate
create table #temp2
(
AccountId Numeric(9,0)
, PaidAmount Numeric(10, 2)
, PaymentDate date
)
select a.accountid, sum(a.Paidamount), max(a.PaymentDate)
from tblCreditDefenseInceptionToDateBenefit a
inner join #temp1 b on a.accountid = b.accountid
where a.paymentdate <= (select top 1 StatementDate from Statements
where AccountID = a.accountid
and statementdate >= b.dateofdeath
order by StatementDate desc)
and a.paymentdate > (select top 1 StatementDate from Statements
where AccountID = a.accountid
and statementdate < b.dateofdeath
order by StatementDate desc)
group by a.accountid
order by a.accountid desc
select * from #temp2
drop table #temp1
drop table #temp2
you can go about it a few ways
Create table #accounts
(AccountID int, Date_Death date)
insert into #accounts
(accountID, Date_death)
values
('1001', '03/10/2014')
Create Table #payments
(AccountID int, paidamt decimal(6,2), paymentdt date)
insert into #payments
(AccountID , paidamt, paymentdt)
values
('1001', '80.27','07/09/2014'),
('1001', '80.27','06/10/2014'),
('1001', '80.27','05/12/2014'),
('1001', '80.27','04/13/2014'),
('1001', '80.27','03/15/2014'),
('1001', '80.27','02/14/2014')
;
with cte as (
select
Accountid,
case when paymentdt between '02/25/2014'and '03/28/2014' then (paidamt) else null end as paidamt
from
#payments
)
Select
accountid,
SUM(paidamt)
from cte
group by
AccountID
or
put it in the where clause instead of doing a case statement, really depends onyour style
select
accountid,
sum(paidamt)paidamt
from
#payments
where paymentdate >= '02/25/2014'
and paymentdate <= '03/282014'
or
if you want to use the statement table dates as parameters
with cte as
(
select
a.AccountID,
case when a.paymentdt between b.min_dt and b.max_dt then a.paidamt else null end as 'pdamt'
from
#payments as a
inner join
(select accountid, MIN(statementdt)min_dt, MAX(statementdt)max_dt from #statement group by accountid) as b on b.accountid = a.AccountID
)
select
AccountID,
SUM(pdamt) as 'Paid Amount'
from
cte
group by
AccountID
again, could be added in where clase if you dontwant to do case staements

How to get latest value from table with self inner join

Please see http://sqlfiddle.com/#!6/9254d/3/0
I have two tables, Person and Values, PersonID is the link between them. Each person in the Values table has multiple values per day for every hour. I need to get the latest value for each user. I had a look on SO and what I could find was to get MAX(ValueDate) and then join on that but doesn't work. Join on PersonID didn't work either, not sure what else to try.
The output I need is
Name Value
1fn 1ln 2
2fn 2ln 20
3fn 3ln 200
I don't need the greatest value, I need the latest value for each person. Please share if you have any ideas. Thanks.
Try this:
SQLFIDDLEExample
DECLARE #Org nvarchar(3)
SELECT #Org = 'aaa'
DECLARE #MyDate date
SELECT #MyDate = CONVERT(date, '2014-09-12')
SELECT a.Name,
a.Value as Revenue
FROM(
SELECT p.FName + ' ' + p.LName AS Name,
vt.Value,
ROW_NUMBER()OVER(PARTITION BY vt.PersonID ORDER BY vt.ValueDate desc) as rnk
FROM Person p
LEFT JOIN ValueTable vt
ON vt.PersonID = p.PersonID
WHERE vt.ValueDate < DATEADD(day,1,#MyDate)
AND vt.ValueDate >= #MyDate
AND vt.Org = #Org)a
WHERE a.rnk = 1
ORDER BY a.Name ASC
Result:
| NAME | REVENUE |
|---------|---------|
| 1fn 1ln | 2 |
| 2fn 2ln | 20 |
| 3fn 3ln | 200 |