Compare field values from different users - sql

Ok so this has been doing my head in for a while and the code has gone through a few different iterations.
I need to know if a technician has changed a time on service call and report a TRUE or FALSE statement.
Using the below example, the user of ID 60 IS NOT a technician and user 29 IS a technician.
We can see in log instance 10 that the technician (user 29) changed the time from the preexisting value that user id 60 had set (log instance 7) so this would report as TRUE. Otherwise, if there are no instances of changes by a technician I would instead need to return a false value.
Any other entries are irrelevant here, I just need to compare the start and end times from a technician with the last start and end time entries from a non-technician before hand (nothing afterwards).
If there are no entries by a technician it should still report a FALSE value.
A technician can be identified by the roleID of -2 on the HEM6 table which i have included. This query will become a subquery of a main query joined by the callID.
Apologies if this request is a bit wordy, happy to clarify if I can.
SELECT
T1.StartTime AS [Current start time],
T1.endTime AS 'Current End Time',
T2.LogInstanc,
T2.usersign2
FROM
ASC6 T1
INNER JOIN
ASCL T2 ON T2.LogInstanc = T1.[logInstanc]
AND T2.callID = T1.SrcvCallID
INNER JOIN
OUSR T3 ON T3.INTERNAL_K = T2.usersign2
INNER JOIN
OHEM T4 ON T4.userid = T3.userid
LEFT JOIN
HEM6 T5 ON T5.empID = T4.empID
WHERE
T1.srcvcallID = 25557
Results from this query:
Start Time
End Time
Log Instance
Updating User
8:54
9:09
2
1
15:30
16:00
3
60
15:30
16:00
4
60
15:30
16:30
5
60
15:30
17:00
6
60
15:30
16:30
7
60
15:30
16:30
8
29
15:30
16:30
9
29
15:45
16:45
10
29
15:45
16:45
11
29
15:45
16:45
12
60

Ok so i think i got this figured out, all the tests so far have implied it works.
SELECT COUNT(*)
FROM
(
SELECT
A.startTime AS [StartTime],
A.endTime AS [EndTime],
B.LogInstanc,
B.usersign2,
LAG(A.startTime,1) OVER(ORDER BY B.Loginstanc ASC) AS 'ChangedStartTime',
LAG(A.endTime,1) OVER(ORDER BY B.Loginstanc ASC) AS 'ChangedEndTime'
FROM ASC6 A
INNER JOIN ASCL B ON B.LogInstanc = A.[logInstanc] AND B.callID = A.SrcvCallID
INNER JOIN OUSR C ON C.INTERNAL_K = B.usersign2
INNER JOIN OHEM D ON D.userid = C.userid
LEFT JOIN HEM6 E ON E.empID = D.empID
WHERE A.srcvcallID = T0.callID AND
A.Technician <> 104 AND
A.Technician IS NOT NULL
)P
WHERE (P.StartTime <> P.ChangedStartTime OR P.endTime <> P.ChangedEndTime) AND
P.usersign2 IN (SELECT S.internal_k FROM OUSR S INNER JOIN OHEM X ON x.userid = s.userid INNER JOIN HEM6 T ON T.empID = X.empID WHERE T.roleID = -2)
GROUP BY P.loginstanc
ORDER BY P.loginstanc DESC
By using the LAG function i've offset the time to indicate where a change occured, as I only need to know if the technician changed the time i've excluded everyone else.
If the technician did change the time then it will display a 1 in the count, otherwise there is no results.

Related

Selecting Rows That Have One Value but Not Another

I need a get some rows of two tables with join that shoud have one value in a column (1407) but shouldn't have other value (1403)
These is the tables and the query:
select a.job, a.date, b.group from log a inner join active_tmp b
on a.jobno=b.jobno and a.no=b.no where b.list = 'N'
AND LOGDATE = TO_CHAR(TRUNC(SYSDATE),'YYYYMMDD')
and a.job not like 'HOUSE%'
and a.job not like 'CAR%' and (errorCode=1047 and errorCode<>1403);
LOG
JOB DATE LOGDATE JOBNO NO errorCode
MAM 20220123 20220125 33 22 1047
MAM 20220123 20220125 33 22 1403
DAD 20220122 20220125 11 99 1047
MAM 20220122 20220125 33 22 0323
DAD 20220122 20220125 11 99 0444
ACTIVE_TMP
JOB JOBNO NO GROUP LIST
MAM 33 22 LAPTOP N
MAM 33 22 LAPTOP N
DAD 11 99 KEY N
But I get:
MAM,20220123,LAPTOP
DAD,20220122,KEY
I need:
DAD,20220122,KEY
Because MAM have both codes (1047 and 1043).
To rephrase, I think you mean "I want to return matching rows that have error code 1047 but for which the same values of jobno, no, list do not have a corresponding row with error code 1403"
This part is redundant:
AND (errorCode = 1047 AND errorCode <> 1403);
If you are saying errorCode must be 1047, you are also saying it is not equal to 1403.
I think you want to select some rows into some result set, then check that there's not another row that disqualifies one of the selected rows from the final result.
So,
SELECT a.job,
a.date,
b.group
FROM _log a
INNER JOIN _active_tmp b
ON a.jobno = b.jobno
AND a.no = b.no
WHERE b.list = 'N'
AND LOGDATE = TO_CHAR(CURRENT_TIMESTAMP,'YYYYMMDD')
AND a.job NOT LIKE 'HOUSE%'
AND a.job NOT LIKE 'CAR%'
AND a.errorCode = 1047
AND NOT EXISTS (SELECT 1
FROM _log c
INNER JOIN _active_tmp d
ON c.jobno = d.jobno
AND c.no = d.no
WHERE a.job = c.job
AND a.date = c.date
AND b.group = d.group
AND c.errorCode = 1403)
We select the rows that satisfy the join and have error code 1047 then subtract from that set those rows that also satisfy the join but have error code 1403. You could possibly make this more terse using CTE or a temp table, but this works too.
Note I had to change a few things to make it work in my engine (Postgres), so you may have to change a few things back to Oracle.
You need to change the error code logic. Identify what JOB values has 1403 and then exclude those values
select distinct a.job, a.date, b.[group] from LOG a inner join active_tmp b
on a.jobno=b.jobno and a.no=b.no where b.list = 'N'
AND LOGDATE = TO_CHAR(TRUNC(SYSDATE),'YYYYMMDD')
and a.job not like 'HOUSE%'
and a.job not like 'CAR%' and a.job not in (select JOB from log where errorCode in(1403));

postgres: dynamic subquery in join filtering

Currently trying to define a dynamic query in Postgres. Essentially the filtering done by a subquery used in the Join depends on the results for each row. The idea is that each “name” in the query will only return one project_id based on a specific set of parameters.
The basic query without any filtering:
SELECT a.name, rproj.raproject_id, rproj.current_status, rproj.create_date
FROM focalpoint fp
JOIN agl_asset a ON a.serial_number::text = fp."RoutineNumber"::text
JOIN agl_raproject AS rproj ON a.asset_id = rproj.asset_id
JOIN agl_auditproject AS audit ON rproj.auditproject_id = audit.auditproject_id
ORDER BY a.name
That would return:
name raproject_id current_status create_date
AssetA 405323966463427000 Review 24/10/2014 18:35
AssetA 405323966463460000 Review 07/10/2016 14:04
AssetA 405323966463413000 Risk Identification 28/11/2013 14:16
AssetA 405323966463413000 Closed 21/11/2013 17:33
AssetB 405323966463412000 Monitoring 15/11/2013 11:26
AssetB 405323966463427000 Review 24/10/2014 18:35
AssetB 405323966463461000 Assessment 13/10/2016 10:32
AssetB 405323966463412000 Closed 15/11/2013 11:44
But I only want one “project” per asset. If I was just trying to get the “newest” based on the create_date it would be:
SELECT a.name, rproj.raproject_id, rproj.current_status, rproj.create_date
FROM focalpoint fp
JOIN agl_asset a ON a.serial_number::text = fp."RoutineNumber"::text
JOIN agl_raproject AS rproj ON a.asset_id = rproj.asset_id AND rproj.create_date = ((SELECT max(rproj2.create_date) AS max
FROM agl_raproject rproj2
JOIN agl_auditproject audit ON rproj2.auditproject_id = audit.auditproject_id
WHERE a.asset_id = rproj2.asset_id AND audit.project_type::text = 'ngERMAssessment'::text))
JOIN agl_auditproject AS audit ON rproj.auditproject_id = audit.auditproject_id
ORDER BY a.name
But what I need is:
If there’s any project, for this specific asset, where the
current_status is “Monitoring” – Return that row
If not, take the newest one (as I’ve done already on the last query).
But again, only one project from raproject should be returned per asset.
EDIT:
The expected return would be:
name raproject_id current_status create_date
AssetA 405323966463460000 Review 07/10/2016 14:04
AssetB 405323966463412000 Monitoring 15/11/2013 11:26
distinct on
select distinct on (a.name)
a.name, rproj.raproject_id, rproj.current_status, rproj.create_date
from
focalpoint fp
inner join
agl_asset a on a.serial_number::text = fp."RoutineNumber"::text
inner join
agl_raproject as rproj on a.asset_id = rproj.asset_id
inner join
agl_auditproject as audit on rproj.auditproject_id = audit.auditproject_id
order by a.name, rproj.current_status <> 'Monitoring', rproj.create_date desc
Check the order by requirement

SELF-JOIN discarding true CROSS JOIN rows

I have the following query;
What I get is tickets information. I use self-join to obtain the requester and the assignee in the same row:
SELECT z.id AS TICKET, z.name AS Subject, reqs.name AS Requester, techs.name AS Assignee,
e.name AS Entity,DATE_FORMAT(tt.date,'%y%-%m%-%d') AS DATE,
DATE_FORMAT(tt.date,'%T') AS HOUR,
CASE WHEN z.priority = 6 THEN 'Mayor' WHEN z.priority = 5 THEN 'Muy urgente' WHEN z.priority = 4 THEN 'Urgente'WHEN z.priority = 3 THEN 'Mediana' WHEN z.priority = 2 THEN 'Baja' WHEN z.priority =1 THEN 'Muy baja' END AS Priority,
c.name AS Category, i.name AS Department
FROM glpi_tickets_users tureq
JOIN glpi_tickets_users tutech ON tureq.tickets_id = tutech.tickets_id
JOIN glpi_users AS reqs ON tureq.users_id = reqs.id
JOIN glpi_users AS techs ON tutech.users_id = techs.id
JOIN glpi_tickets z ON z.id = tureq.tickets_id
LEFT OUTER JOIN glpi_tickettasks tt ON z.id = tt.tickets_id
LEFT JOIN glpi_itilcategories i ON z.itilcategories_id = i.id
LEFT JOIN glpi_usercategories c ON c.id = reqs.usercategories_id
INNER JOIN glpi_entities e ON z.entities_id = e.id
WHERE (tureq.id < tutech.id AND tureq.type < tutech.type) OR
(tureq.id < tutech.id AND tureq.users_id = tutech.users_id) OR
(tureq.id = tutech.id AND tureq.users_id = tutech.users_id)
The problem is that I get something like that:
1 Report jdoe jdoe Development 16-06-07 11:56:17 Mediana Software Mkt
1 Report jdoe fwilson Development 16-06-07 11:56:17 Mediana Software MKt
1 Report fwilson fwilson Development 16-06-07 11:56:17 Mediana Software Mkt
2 Task11 gwilliams gwilliams Ops 16-06-08 12:00:00 ALTA Hardware Def
3 Task12 gwilliams gwilliams Ops 16-06-08 12:01:00 ALTA Hardware Def
I don't want first and third row because is a CROSS JOIN result. Second row is OK, because jdoe is a requester and fwilson an assignee.
The problem is that sometimes requester and assignee are the same, eg: he creates a ticket for a task that himself will do. For example, 4th and 5th rows are OK.
So, how should I do to make a difference for those distinct cases, i.e.: I need to include:
tureq.id = tech.id AND req.users_id = tech.users.id
BUT NOT IF ALREADY EXISTS
tureq.id = tech.id AND req.users_id <> tech.users_id
Update
The main problem is that a user can assign to himself a ticket:
SELECT * from glpi_tickets_users WHERE type = 2 GROUP BY tickets_id HAVING COUNT(users_id)<2 limit 3;
+----+------------+----------+------+------------------+-------------------+
| id | tickets_id | users_id | type | use_notification | alternative_email |
+----+------------+----------+------+------------------+-------------------+
| 1 | 2 | 12 | 2 | 1 | NULL |
| 3 | 6 | 13 | 2 | 1 | NULL |
| 7 | 8 | 14 | 2 | 1 | NULL |
+----+------------+----------+------+------------------+-------------------+
Update 2:
It was a human mistake. The problem was really not about self-assigned tickets. Rather it was either that some tickets had not Requester or had Requester but still had not any resolver assigned.
I've found
As there are always the two types per ticket you are interested in, you can simply select the according records, so as to get requester and assignee per ticket.
select
t.id as ticket,
t.name as subject,
requester.name as requester,
assignee.name as assignee,
e.name as entity,
date_format(tt.date,'%y%-%m%-%d') as date,
date_format(tt.date,'%T') as hour,
case t.priority
when 6 then 'Mayor'
when 5 then 'Muy urgente'
when 4 then 'Urgente'
when 3 then 'Mediana'
when 2 then 'Baja'
when 1 then 'Muy baja'
end as priority,
uc.name as category,
ic.name as department
from glpi_tickets t
join glpi_entities e on e.id = t.entities_id
join
(
select tu.tickets_id, u.name, u.usercategories_id
from glpi_tickets_users tu
join glpi_users u on u.id = users_id
where tu.type = 1
) requester on requester.tickets_id = t.id
join
(
select tu.tickets_id, u.name
from glpi_tickets_users tu
join glpi_users u on u.id = users_id
where tu.type = 2
) assignee on assignee.tickets_id = t.id
left join glpi_itilcategories ic on ic.id = t.itilcategories_id
left join glpi_usercategories uc on uc.id = requester.usercategories_id;
left outer join glpi_tickettasks tt on tt.tickets_id = t.id
The only thing I wonder is: There can be several ticket tasks per ticket. So what do you want to do then? Have one line per ticket task in your results? This is what the query does. Only, it looks queer that your result rows don't contain any information on the tasks except for the dates, so you may have many, many lines with the same data, only with different dates. So maybe, you'd rather want the first or last date per ticket. To get the last date per ticket, you'd replace the last line in the query with:
left outer join
(
select tickets_id, max(date) as date
from glpi_tickettasks
group by tickets_id
) tt on tt.tickets_id = t.id
And you probably want to add an ORDER BY clause.
you need to add more qualifiers to your joins for example
JOIN glpi_tickets_users tutech ON tureq.tickets_id = tutech.tickets_id and tutech.type = 2

DB2 for IBM i (AS400) - SQL

Two tables A (daily Loan Transaction table) and B (daily eod Loan Balance table).
Trying to write a select statement in DB2 AS400 to display daily loan transactions, eod Loan Balance and beginning loan balance (prior day) for selected date range.
Below query would give me daily loan transactions and eod loan balance. But need some help on modifying the query below to calculate beginning loan balance as well (balance as of 02/28/2015) for date range 03/01/2015 to 03/31/2015 selected.
select A.*, -- daily loan transactions
B.EOD_Loan_Balance
from A
inner join B
on A.date_id = B.date_id
and A.Loan_num = B.Loan_num
where a.date_id between 03/01/2015 to 03/31/2015
Note that previous day should be business day so if 02/28/2015 is not a business day then we need to pull eod balance of day before that and which should be a business day.
Any idea would be appreciated.
Thank you!
This is one of the many tasks that are easy to do with a "calender" or "dates" table...
select D.*,
B.EOD_Loan_Balance,
E.EOD_Loan_Balance,
from MY_CALENDAR_TABLE C
join daily_trans D on c.date = d.date
join eod_bal B on c.date - 1 day= b.date and d.loan_num = b.loan_num
join eod_bal E on c.date = e.date and d.loan_num = e.loan_num
where c.date between 2015-03-01 and 2015-03-31
and c.is_business_day = 1;
--Edit--
Looking back at this, I realized that it doesn't quite work; the join from beginning balance won't give you the prior businsess day.
Luckily, the flexibility of a calendar table comes to the rescue. Just include a PRIOR_BUSINESS_DAY column in your calendar table.
Then the SQL becomes:
select D.*,
B.EOD_Loan_Balance,
E.EOD_Loan_Balance,
from MY_CALENDAR_TABLE C
join daily_trans D on c.date = d.date
join eod_bal B on c.prior_buisiness_day = b.date
and d.loan_num = b.loan_num
join eod_bal E on c.date = e.date
and d.loan_num = e.loan_num
where c.date between 2015-03-01 and 2015-03-31
and c.is_business_day = 1;
Would this work for you?
select A.*, -- daily loan transactions
B.EOD_Loan_Balance,
C.EOD_Loan_Balance,
from A
inner join B
on A.date_id = B.date_id
and A.Loan_num = B.Loan_num
LEFT OUTER join C
on A.Loan_num = C.Loan_num
where a.date_id between '2015-03-01' to '2015-03-31'
and c.date_id = CASE
WHEN DAYOFWEEK('2015-03-01') = 1 THEN '2015-03-01' - 2 days 'Sunday --> Friday
WHEN DAYOFWEEK('2015-03-01') = 2 THEN '2015-03-01' - 3 days 'Monday --> Friday
ELSE '2015-03-01' - 1 day ' Previous day
END
Untested as I don't have a DB2 instance handy.
EDIT Revised to allow for weekends as mentioned in comments.

Constructing an SQL query for schema

I have the following database schema for an attendance system:
How would I write an SQL query to generate a good report of entries on day X? I need it to generate a report that has
Employee Name | TimeIn | TimeOut
Bob | 10:00 | 11:00
Sam | 10:30 | 18:00
Bob | 11:30 | 15:00
but the row that defines if it was a time in or out is set by entryType (1 being in, 0 being out), so I would aliases TimeIn and TimeOut.
My attempt was
`SELECT firstName, time from log INNER JOIN users on log.employeeID = users.employeeID WHERE date = GETDATE()`
but this doesn't handle the fact that some times are entry, some are exit.
Note that there can be multiple sign ins per date.
Update:
Another attempt, but the subquery returns multiple rows
select firstName, (select time as timeIn from log where entryType = 1), (select time as timeOut from log where entryType = 0) inner join users on log.uID = users.uID from log group by uID
This works in Oracle (apologies for the non-ANSI style, but you should get the drift)..
SELECT FORENAME,SURNAME,L1.TIME IN_TIME,L2.TIME OUT_TIME
FROM EMPLOYEES EMP, LOG L1, LOG L2
WHERE EMP.EMPLOYEE_ID = L1.EMPLOYEE_ID
AND EMP.EMPLOYEE_ID = L2.EMPLOYEE_ID
AND L1.ENTRYTYPE = 1
AND L2.ENTRYTYPE = 0
AND L2.TIME = (SELECT MIN(TIME) FROM LOG WHERE EMPLOYEE_ID = L2.EMPLOYEE_ID AND L2.ENTRYTYPE = 0 AND TIME > L1.TIME)
Update:
Ah, yes, hadn't considered that. In this case you need an outer join. something like this (untested):
SELECT FORENAME,SURNAME,L1.TIME IN_TIME,L2.TIME OUT_TIME
FROM EMPLOYEES EMP
INNER JOIN LOG L1 ON EMP.EMPLOYEE_ID = L1.EMPLOYEE_ID AND L1.ENTRYTYPE = 1
LEFT OUTER JOIN LOG L2 ON EMP.EMPLOYEE_ID = L2.EMPLOYEE_ID AND L2.ENTRYTYPE = 0
AND L2.TIME = (SELECT MIN(TIME) FROM LOG WHERE EMPLOYEE_ID = L2.EMPLOYEE_ID AND L2.ENTRYTYPE = 0 AND TIME > L1.TIME)
Simply this will work. Try this
SELECT FORENAME,SURNAME,LG.IN_TIME,LG.OUT_TIME FROM EMPLOYEES EMP INNER JOIN
(SELECT MIN(TIME) IN_TIME,MAX(TIME) OUT_TIME,EMPLOYEE_ID FROM LOG
GROUP BY EMPLOYEE_ID) LG ON EMP.EMPLOYEE_ID=LG.EMPLOYEE_ID
Note : I didnt include the entry type because at any time min time will be swipe in and max time will be swipe out
Updated
To show no of sign ins and outs try something like this,
SELECT FORENAME,SURNAME,LG.IN_TIME,LG.OUT_TIME,LG.no_of_ins,
LG.no_of_outs FROM EMPLOYEES EMP INNER JOIN
(SELECT MIN(TIME) IN_TIME,MAX(TIME) OUT_TIME,EMPLOYEE_ID,
COUNT( CASE WHEN ENTRY_TYPE='I' THEN 1 ELSE O END noi) no_of_ins,
COUNT( CASE WHEN ENTRY_TYPE='O' THEN 1 ELSE O END nou) no_of_outs,
GROUP BY EMPLOYEE_ID) LG ON EMP.EMPLOYEE_ID=LG.EMPLOYEE_ID
This query will give you the earliest time in and latest time out of an employee.
SELECT E.FORENAME,
(SELECT MIN(TIME) FROM LOG WHERE EMPLOYEEID = E.EMPLOYEEID AND ENTRYTYPE = 1 AND DATE = <YOUR DAYE>) AS "TIME_IN",
(SELECT MAX(TIME) FROM LOG WHERE EMPLOYEEID = E.EMPLOYEEID AND ENTRYTYPE = 0 AND DATE = <YOUR DAYE>) AS "TIME_OUT"
FROM EMPLOYEE E WHERE E.EMPLOYEEID = <EMPLOYEE ID>