Issues with Criteria in SQL - sql

I am really hoping someone can help me, i have a table as such:
Period Client Active
jan-20 x 1
feb-20 y 0
mar-21 z 1
jan-21 t 0
mar-21 f 1
jan-21 f 0
dec-20 f 0
The period is as format 'YYY-MM-DD'.
I have a table (x) that has multiple periods, clients and an active status of 1 or 0.
I want to pull in only clients where the current month active status is '1' but the active status for all previous months of the current month are '0'.
what i am getting at the moment, is results that show the client, but where i dive deeper into that client, it has months where active status = '1' in previous months, but them periods are excluded from my results, when in actual fact that specific client shouldn't be in my results at all.
FYI - my current period will always be the MAX period within my table (x).
This is the code i am currently using - but is not working as pulling in clients that shouldnt be in there.
*SELECT
e.EmeaChannel,
fc.PeriodKey,
fc2.PeriodKey,
fc.Merchantkey AS Clients,
fc.RevenueActive,
fc2.RevenueActive
FROM [Fact].[ClientCountFull] fc
inner join [Fact].[ClientCountFull] FC2 on
FC2.PeriodKey >= DATEADD(MONTH, -16, FC.PeriodKey)
AND FC2.PeriodKey < FC.PeriodKey
AND FC.MerchantKey = FC2.MerchantKey
AND fc.EmeaChannelKey = fc2.EmeaChannelKey
inner join Dimension.EmeaChannel e on e.emeachannelkey = fc.emeachannelkey
inner join dimension.account a on a.MerchantKey = fc.MerchantKey
where
AccountStatus = 'Open'
and e.EmeaChannel = 'CMS'
and fc.RevenueNew = 1
and fc.RevenueActive = 1
and fc2.RevenueActive = 0
order by fc.PeriodKey desc*

If I understand correctly, you can use aggregation. Let me start by assuming that period has a reasonable format of YYYY-MM rather than using the (rather useless) month name. Then you can use:
select client
from t
where active = 1
group by client
having min(period) = '2021-03';
In your case, you will need to convert the period to a date -- or at least to YYYY-MM format. That depends on the database you are using, and date/time functions are notoriously database-dependent.

Related

SQL query to find the average time differences between two statuses

I am trying to find the time between status changes for tickets. The statuses are A,B,C,D,E. I need to identify where the bottlenecks are in the system. The table looks something like this:
ticket_no
created_at
current_status
next_status
1
12/2/2022
A
B
1
12/3/2022
B
C
1
12/3/2022
C
B
1
12/4/2022
B
C
1
12/4/2022
C
E
2
12/4/2022
A
C
2
12/5/2022
C
D
2
12/7/2022
D
E
As you can see for ticket 1, it cycled between statuses B and C before finally ending at E. I want to calculate the average time tickets take to move between specific statuses (say A->C, C->E). It’s a bit confusing because tickets can return to previous statuses and tickets don’t need to move through every status. There is an order to the statuses but you can return to a previous state.
Any ideas?
I’ve tried a bunch of things, like lagging (only looks at previous/next), or even pivoting with case statements and subtracting but it doesn't seem to work.
Again the ask is to find the time spent (on average) to go between 2 specific statuses, such as A->C or C->E
Here's my query so far. The idea is to pivot things and just subtract, but I'm really not sure this is gonna be valid
with pv_times as (
select ticket_no,
max(case when current_status='A' and next_status='B' then created_at else null end) as ab_time,
max(case when current_status='A' and next_status='C' then created_at else null end) as ac_time
FROM statuses
GROUP BY 1
)
select * from pv_times
# subtract times to find diff...but is this even valid?
time spent to go between 2 specific statuses
Enumerate all such statuses.
This is the lower diagonal triangle of a 5 × 5 matrix.
Then do a JOIN (.merge) to aggregate all observed
transitions against that vector of possibilities,
.count()'ing the number of them we observed.

How do I stop my query from pulling duplicates?

Yes, I know this seems simple:
SELECT DISTINCT(...)
Except, it apparently isn't
Here is my actual Query:
SELECT
DeclinationReasons.Reason,
EmployeeInformation.ID,
EmployeeInformation.Employee,
EmployeeInformation.Active,
CompletedTrainings.DecShotDate,
CompletedTrainings.DecShotLocation,
CompletedTrainings.DecReason,
CompletedTrainings.DecExplanation,
IIf([DecShotLocation]="MCS","Yes","No") AS YesMCS,
IIf([DecReason]=1,1,0) AS YesAllergy,
IIf([DecReason]=2,1,0) AS YesImmune,
IIf([DecReason]=3,1,0) AS YesAdverse,
IIf([DecReason]=4,1,0) AS YesMedical,
IIf([DecReason]=5,1,0) AS YesSpiritual,
IIf([DecReason]=6,1,0) AS YesOther,
IIf([DecReason]=7,1,0) AS YesAlready
FROM
EmployeeInformation
INNER JOIN (CompletedTrainings
LEFT JOIN DeclinationReasons ON CompletedTrainings.DecReason = DeclinationReasons.ReasonID)
ON EmployeeInformation.ID = CompletedTrainings.Employee
GROUP BY
DeclinationReasons.Reason,
EmployeeInformation.ID,
EmployeeInformation.Employee,
EmployeeInformation.Active,
CompletedTrainings.DecShotDate,
CompletedTrainings.DecShotLocation,
CompletedTrainings.DecReason,
CompletedTrainings.DecExplanation,
IIf([DecShotLocation]="MCS","Yes","No"),
IIf([DecReason]=1,1,0),
IIf([DecReason]=2,1,0),
IIf([DecReason]=3,1,0),
IIf([DecReason]=4,1,0),
IIf([DecReason]=5,1,0),
IIf([DecReason]=6,1,0),
IIf([DecReason]=7,1,0)
HAVING
((((EmployeeInformation.Active) Like -1)
AND ((CompletedTrainings.DecShotDate + 365 >= DATE())
OR (CompletedTrainings.DecShotDate IS NULL))));
This is Joining a few tables (obviously) in order to get a number of records. The problem is that if someone is duplicated on the table with a NULL in one of the date fields, and a date in another field, it pulls both the NULL and the DATE, or pulls multiple NULLS it might pull multiple dates but those are not present right at the moment.
I need the Nulls, they are actual data in this particular case, but if someone has a date and a NULL I need to pull only the newest record, I thought I could add MAX(RecordID) from the table, but that didn't change the results of the query either.
That code:
SELECT
DeclinationReasons.Reason,
EmployeeInformation.ID,
EmployeeInformation.Employee,
EmployeeInformation.Active,
MAX(CompletedTrainings.RecordID),
CompletedTrainings.DecShotDate
...
And it returned the same issue, Duplicated EmployeeInformation.ID with different DecShotDate values.
Currently it returns:
ID
Active
DecShotDate
etc. x a bunch
1
-1
date date
whatever goes
2
-1
in these
2
-1
date date
columns
These are being used in a report, that is to determine the total number of employees who fit the criteria of the report. The NULLs in DecShotDate are needed as they show people who did not refuse to get a flu vaccine in the current year, while the dates are people who did refuse.
Now I have come up with one simple solution, I could add a column to the CompletedTrainings Table that contains a date or other value, and add that to the HAVING statement. This might be the right solution as this is a yearly training questionnaire that employees have to fill out. But I am asking for advice before doing this.
Am I right in thinking I need to add a column to filter by so that older data isn't being pulled, or should I be able to do this by pulling recordID, and did I just bork that part of the query up?
Edited to add raw table views:
EmployeeInformation Table:
ID
Last
First
empID
Active
Termdate
DoH
Title
PT/FT/PD
PI
1
Doe
Jane
982
-1
date
Sr
PD
X
2
Roe
John
278
0
date
date
Jr
PD
X
3
Moe
Larry
1232
-1
date
Sr
FT
X
4
Zoe
Debbie
1424
-1
date
Sr
PT
X
DeclinationReasons Table:
ReasonID
Reason
1
Allergy
2
Already got it
3
Illness
CompletedTrainings Table:
RecordID
Employee
Training
...
DecShotdate
DecShotLocation
DecShotReason
DecExp
1
1
4
date
location
2
text
2
1
4
3
2
4
4
3
4
date
location
3
text
5
3
4
date
location
1
text
6
4
4
After some serious soul searching, I decided to use another column and filter by that.
In the end my query looks like this:
SELECT *
FROM (
(
SELECT RecordID, DecShotDate, DecShotLocation, DecReason, DecExplanation, Employee,
IIf([DecShotLocation]="MCS","Yes","No") AS YesMCS, IIf([DecReason]=1,1,0) AS YesAllergy,
IIf([DecReason]=2,1,0) AS YesImmune, IIf([DecReason]=3,1,0) AS YesAdverse,
IIf([DecReason]=4,1,0) AS YesMedical, IIf([DecReason]=5,1,0) AS YesSpiritual,
IIf([DecReason]=6,1,0) AS YesOther, IIf([DecReason]=7,1,0) AS YesAlready
FROM CompletedTrainings WHERE (CompletedDate > DATE() - 365 ) AND (Training = 69)) AS T1
LEFT JOIN
(
SELECT ID, Active FROM EmployeeInformation) AS T2 ON T1.Employee = T2.ID)
LEFT JOIN
(
SELECT Reason, ReasonID FROM DeclinationReasons) AS T3 ON T1.DecReason = T3.ReasonID;
This may not have been the best solution, but it did exactly what I needed. Which is to get the information by latest entry into the database.
Previously I had tried to use MAX(), DISTINCT(), etc. but always had a problem of multiple records being retrieved. In this case, I intentionally SELECT the most recent records first, then join them to the results of the next query, and so on. Until I have all the required data for my report.
I write this in hopes someone else finds it useful. Or even better if someone tells me why this is wrong, so as to improve my own skills.

Finding the exact overlapping time

with tickets as (
select o.SSTID, o.open_Id, o.Createddatetime openTime, c.Createddatetime closeTime
from dbo.Close_ticket c
inner join dbo.Openticket o ON o.SSTID = c.SSTID and c.Open_ID=o.open_id
)
select t1.SSTID,
SUM(isnull(datediff(hour
, case when t1.openTime > t2.openTime then t1.openTime else t2.openTime end
, case when t1.closeTime > t2.closeTime then t2.closeTime else t1.closeTime end),0)) as [OverLappingtime]
from tickets t1
left join tickets t2 on t1.SSTID = t2.SSTID
and t1.openTime < t2.closeTime and t2.openTime < t1.closeTime
and t1.open_id < t2.open_id
group by t1.SSTID
This is my code where each ticket is compared to every other ticket to find the total overlapping time. But if I create more tickets the total time exceeds 24 hours when all the tickets where created on the same day. How can I find the exact overlapping time? If we see the first three tickets, the 2nd and the third ticket were opened and closed within the opening and closing time of the first ticket.
I need the exact overlapping time.
This is my Openticket table.
[Open_ID,SSTID,Createddatetime]
- 1,1,2020-04-27 06:40:32.337
- 2,1,2020-04-27 12:40:32.337
- 3,1,2020-04-27 14:40:32.337
- 4,1,2020-04-27 15:40:32.337
- 5,1,2020-04-27 18:40:32.337
This is my Close_ticket table.
[Close_id,open_id,SSTID,Createddatetime]
- 1,1,1,2020-04-27 20:40:32.337
- 2,2,1,2020-04-27 15:40:32.337
- 3,3,1,2020-04-27 16:40:32.337
- 4,4,1,2020-04-27 17:40:32.337
- 5,5,1,2020-04-27 21:40:32.337
You keep saying "the logic I've used so far is the one I mentioned" but at no point have you actually mentioned this logic in any useful form so that anyone can understand what it is you are doing: all you are doing is stating numbers with no indication on how you calculated these numbers.
Please provide a step by step guide to show how you calculated an overlap figure of 4 hours for the first 3 tickets.
For example, taking your data but moving the start/end times to the hour (rather than 40:32.337) for the sale of simplicity, we have this:
Possible overlap calculations:
2 overlaps 1 by 3 hours => overlap is 3
3 overlaps 1 by 3 hours => overlap is 3
You want to calculate overlap of both 2 & 3 compared to 1: 3 + 3 = 6
You only want the overlap when all 3 tickets overlap: 1
You don't want to double count any overlap: 2 overlaps 1 by 3 hours, 3 overlaps 1 by 3 hours, 2 & 3 overlap each other by 1 hour (double count) => 3 + 3 - 1 = 4
So which of these possible calculations are you using or are you using completely different logic and, if so, what it that logic?

Return data of one Tablix (tablix 1) based on values of another Tabllx (tablix 2)

Good day, I have searched everywhere and seems like the lookup function might be the solution but I need some advise. I have a report with more than one dataset linked to it. Tablix 1 returns a count based on a name field and groups(counts) it per date on specified date range. Tablix 2 returns details on tablix 1's count with more fields including the date per row.
I need to add a "click_link" on Tablix 1 so that when you click on any of the counted values it must return the detailed data from tablix 2 but only if the date matches.
Image of Tablix 1:
Image of Tablix 2:
Image of Tablix 1's data returned without where clause
This is the query for dataset 1(Tablix 1)
DECLARE #Actual int;
DECLARE #Date varchar;
SELECT Actual.Actual,Actual.DateActual,Original.Original,Original.DateOriginal FROM
(
Select count (payhist.AC_CODEID) as Actual,PAYHIST.PH_DATE as DateActual from PAYHIST
join Paymonth on PAYHIST.PH_DATE >= Paymonth.ph_datesd and PAYHIST.PH_DATE <= Paymonth.ph_dateed
Join EMPLOYEE E on PAYHIST.MST_SQ = e.MST_SQ
Join Worklocation wl on e.wl_codeid = wl.wl_codeid
Join Paymonth pm on PAYHIST.PH_DATE >= pm.ph_datesd and PAYHIST.ph_date <= pm.ph_dateed
where Paymonth.CurrentPD = 1
and PH_EXCEPTION = 1
and e.EMP_DISCHARGE is null
and e.EMP_CONTRACTOR = 0
and wl.WL_CODEID = 1
GROUP BY PAYHIST.PH_DATE
) AS Actual
FULL JOIN
(
Select count (PAYHISTTEMP.MST_SQ) as Original,PAYHISTTEMP.PH_DATE as DateOriginal from PAYHISTTEMP
join Paymonth on PAYHISTTEMP.PH_DATE >= Paymonth.ph_datesd and PAYHISTTEMP.PH_DATE <= Paymonth.ph_dateed
Join EMPLOYEE E on PAYHISTTEMP.MST_SQ = e.MST_SQ
Join Worklocation wl on e.wl_codeid = wl.wl_codeid
Join Paymonth pm on PAYHISTTEMP.PH_DATE >= pm.ph_datesd and PAYHISTTEMP.ph_date <= pm.ph_dateed
where Paymonth.CurrentPD = 1
and PH_EXCEPTION = 1
and e.EMP_DISCHARGE is null
and e.EMP_CONTRACTOR = 0
and wl.WL_CODEID = 1
group by PAYHISTTEMP.PH_DATE
) AS Original ON Original.DateOriginal = Actual.DateActual
where (#Actual = '-1' or #Actual = Actual.Actual)
and (#date = '-1' or #date = Original.DateOriginal)
order by DateOriginal Asc
And this is the query for dataset 2
Select e.EMP_EMPNO,e.emp_Firstname,e.emp_surname,d.DPT_NAME,a.AC_NAME,ph.PH_DATE as PAYHIST_DATE,d.DPT_RESPEMP,
ph.PH_FIRSTCLOCK,ph.PH_LASTCLOCK,WL_NAME
into EXCEPREPORT
from PAYHIST PH
Join EMPLOYEE E on ph.MST_SQ = e.MST_SQ
Join DEPARTMENT D on ph.DPT_CODEID = d.DPT_CODEID
join ATTEND A on ph.AC_CODEID = a.AC_CODEID
Join Worklocation wl on e.wl_codeid = wl.wl_codeid
Join Paymonth pm on ph.PH_DATE >= pm.ph_datesd and ph.ph_date <= pm.ph_dateed
where pm.CurrentPD = 1
and PH_EXCEPTION = 1
and e.EMP_DISCHARGE is null
and e.EMP_CONTRACTOR = 0
and e.wl_codeid = 1
select pht.EMP_EMPNO,pht.EMP_FIRSTNAME,pht.EMP_SURNAME, DPT_NAME,e.emp_firstname as RESP_FIRSTNAME,e.emp_surname as RESP_SURNAME,
AC_NAME,PAYHIST_DATE,PH_FIRSTCLOCK,PH_LASTCLOCK,WL_NAME as WORKLOCATION
from EXCEPREPORT pht
join employee e on pht.dpt_respemp = e.mst_sq
drop table EXCEPREPORT
#Harry, I am completely confused now...Should the parameters be added on the summary or details tablix? I am only interested in the details of the outstanding transactions. Which I get from the summary tablix's data set. The query returns a count of one of the columns namely(AC_CODE) as well as date and the same query (using full join) to run two select queries each to different tables does a count on a similar column and date(Results as link of picture above). The details are then returned with Tablix 2's data set which is a different query and there are no identical column names except the date which is the same on both queries with different column names. Ultimately I want to send this report out as a Excel attachment and if you click on one of the dates on summary in outstanding row it must sort of bookmark to the detail sheet but only show details for relevant date above outstanding number selected. I hope this makes sense?
Please let me know if you require any additional info?
Lets assuming you have two tablix on your report.
TablixA which has the summary
TablixB that has the detail
You would have two datasets (dataset1 for the summary and dataset2 for the detail).
You already have Dataset1 and displaying TablixA without any issues.
What you need to for the details one do is have a couple of extra parameters on your Dataset2. Lets call them #status and #date
Your code / stored procedure for Dataset2 will have at the end of your where clause the following
where....
and (#status = '-1' or #status = yourtable.column_that_holds_status)
and (#date = '1990-01-01' or #date = yourtable.column_that_holds_the_date)
This will create two parameters on your report (Status and date)
Set the default value for these parameters status '-1' and date = '1990-01-01'
Set the initial visibility of TablixB to hidden.
Now on TablixA select the text box that you want to click on (lets call this textbox_click). Right click on textbox_click then go text box properties ->Action. Select "Go to Report". Under Specify a report - select the report that you are currently designing and click Add. Now click on the parameter under Name one at a time until you have all the parameters required for this report covered. Say if you had 1 parameter for TablixA called param1.. so under Name you will have param1 and the value should be set to what the initial parameter was (click on the fx next to value and select that parameter).
Now for the Name Status - select the field (column_that_holds_status) for the field list of dataset1 and date as the field from Dataset1 (column_that_holds_the_date)
Click ok.. assuming everything has been set correctly .. you can now run the report.. and when you move your cursor over the value, it should turn into a finger telling you can click.. click and then it should reload the report.. but now show the second tablix with the correct data in it.
To stop from user clicking on 0 values.. once you have everything working.. go to the "specify a report" then click the fx and use the following
=iif(reportitem!textbox_click.value>0,"Name or your currentreport",0)
It's heaps easier to show then explain.. but hope this gets you started on where you need to get to.
Lets Assume your report is called report1
Lets also assume you have the following result set from dataset1 (ignoring the actual set that is returned)
Bulyanhulu Date value
Original 22 Jan 2018 1
Original 25 Jan 2018 4
Original 26 Jan 2018 6
Original 27 Jan 2018 1
Original 02 Feb 2018 21
Outstanding 22 Jan 2018 0
Outstanding 25 Jan 2018 0
Outstanding 27 Jan 2018 0
Outstanding 02 Feb 2018 6
Cleared 22 Jan 2018 1
Cleared 25 Jan 2018 4
Cleared 27 Jan 2018 6
Cleared 02 Feb 2018 15
The above would be easy to lay out as a matrix (Tablix1)
Now for you second dataset for the details (dataset2)
You need to make sure it is returning the Bulyanhulu column as well as the date column along with other columns that you want for the details tablix( Tablix2)
So for the second data set .. you should have something like this
Select
Bulyanhulu
,Date
,whatever other colums you want
from your_table
where
(#Bulyanhulu = '-1' or #Bulyanhulu = Bulyanhulu)
and
(#date='-1' or #date = Date)
once you have done this to your dataset2, you will see two new parameters :
#Bulyanhulu and #date.
Make them Hidden
Set the default value to -1 for both the parameters
You have two tablix, Tablix1 and Tablix2
Say if we pick on Bulyanhulu-Outstanding for date '02 Feb 2018' (which has a value of 6) on Tablix1
Right click on the text box (lets call it textbox1) and
select properties -> Action
Select Go to report
Specify report1 under "Specify a report"
Click Add to add both your parameters
on the first line for paramenter Bulyanhulu select [Bulyanhulu] for the value
on the Second line for paramenter Date select [Date] for the value
once you have done this.. click on the Fx next to Specify a report and type in the following
=iif(reportitems!textbox1.value>0,"Report1",0)
This ensures that you cannot click on any values that is 0 in tablix 1
Set your Hidden property on Tablix 2 to something like this
=iif(parameters!Bulyanhulu.value = '-1',true,false)
This ensure that on initial load, this details tablix2 is hidden.. it will show only when a value is passed to this parameter by clicking on textbox1
Good luck

Aggregate columns based off the same data

I have the following query -
SELECT d.PRD_YY,
Count(*)
FROM (SELECT CARD,
Min(TXN_DT) mindt
FROM db1.dbo.tblcards
GROUP BY CARD) a
JOIN db1.dbo.tlkpdates d
ON a.MINDT = d.GREG_DT
WHERE d.PRD_YY = 2016
AND d.PRD_NBR = 5
GROUP BY d.PRD_YY
basically, this tells me how many cards from my tblcards table first appeared in a given date range that is taken from the tlkpdates table by joining on mindt from my inner query result.
what I want to do is also see how many cards showed up in that date range altogether, and not just cards whose first occurrence was in that date range.
it doesn't seem like this is possible because i'm joining greg_dt (which is just a normal Gregorian date like 6/1/2016) on the minimum date, so how could i possible join on the maximum date (most recent occurrence)?
i know i can just make another query return that same data set but i'd rather have it in the same query.
edit - what also has to be considered is that i'm going to want to group on more than just PRD_YY - there's also a period number, period week, and period day, for a more granular view.
sample data -
Card Date Store Transaction
10123131444 2014-05-08 25 141414
40999999999 2013-12-07 847 15154
30999999998 2015-02-05 96 234235
20999999997 2016-03-21 139 2342525
50999999996 2016-03-30 659 1234121515
70999999995 2016-03-04 659 52525
50999999994 2016-03-03 907 2362362
20999999993 2014-05-23 941 2623626
70999999992 2013-12-03 18 123124
40999999991 2014-01-18 107 1512515
current output
prd_yy new_cards
2016 22911
desired output
prd_yy new_cards total_cards
2016 22911 54992
Assuming that card is a PK in the tblCards table (or at least only appears once each day at most), I think this will work for what you're trying to do:
SELECT
D.prd_yy,
SUM(CASE WHEN MD.card IS NOT NULL THEN 1 ELSE 0 END) AS new_cards,
COUNT(*) AS total_cards
FROM
dbo.tlkpDates D
INNER JOIN dbo.tblCards C ON C.txn_dt = D.greg_dt
LEFT OUTER JOIN (SELECT card, MIN(txn_dt) AS min_dt FROM dbo.tblCards GROUP BY card) MD ON
MD.card = C.card AND MD.min_dt = C.txn_dt
WHERE
D.prd_yy = '2016' AND
D.prd_nbr = 5
GROUP BY
D.prd_yy
Use the LEFT OUTER JOIN on the minimum dates as a flag for whether or not that particular day is the "first". Then you can use that with SUM(CASE...) to get your conditional count.