How to use count(‘condition') over(partition by xx) in SQL - sql

Here is the database:
The first table is named Trip and the second table is Users.
CLIENT_ID and DRIVER_ID are foreign keys for USERS_ID in the Users table. I want to find how many orders cancelled by the non-banned driver and non-banned passenger for each day (Trip.Status != 'completed' and Users.Banned ='No').
My code is :
SELECT
t.Request_at AS 'Day',
COUNT(Status != 'completed') OVER (PARTITION BY t.Request_at) AS 'Cancellation Num'
FROM
Trips t
JOIN
Users u1 ON t.Client_Id = u1.Users_Id AND u1.Banned = 'No'
JOIN
Users u2 ON t.Driver_Id = u2.Users_Id AND u2.Banned = 'No'
WHERE
t.Request_at >= '2013-10-01' AND t.Request_at <= '2013-10-03'
GROUP BY
t.Request_at
The results for '2013-10-01' and '2013-10-03' are right(both equal to 1). But it turns wrong at '2013-10-02'. It becomes to 1, but it should be 0. I do not know where is the mistake in my code. Could someone help me?

I suspect that you really want conditional aggregation, not a window function:
SELECT t.Request_at AS Day,
SUM(CASE WHEN Status <> 'completed' THEN 1 ELSE 0 END) as num_cancellations
FROM Trips t JOIN
Users u1
ON t.Client_Id = u1.Users_Id AND
u1.Banned = 'No' JOIN
Users u2
ON t.Driver_Id = u2.Users_Id AND u2.Banned = 'No'
WHERE t.Request_at >= '2013-10-01' AND t.Request_at <= '2013-10-03'
GROUP BY t.Request_at;
Note: Only use single quotes for string and date constants. Don't use them for column aliases.

Try this:
SELECT
t.Request_at AS 'Day',
COUNT(case when Status != 'completed' then 1 else null end) OVER (PARTITION BY t.Request_at) AS 'Cancellation Num'
FROM
Trips t
JOIN
Users u1 ON t.Client_Id = u1.Users_Id AND u1.Banned = 'No'
JOIN
Users u2 ON t.Driver_Id = u2.Users_Id AND u2.Banned = 'No'
WHERE
t.Request_at >= '2013-10-01' AND t.Request_at <= '2013-10-03'
GROUP BY
t.Request_at

Related

Restructure query to group by user_id and work out average time to hire

I have the following query that works out the time to hire in days for all ad_id's based on a given USER_PK_ID
SELECT ca.ad_id, r.CAND_ID, u.USER_PK_ID, u.user_id, round(ca.CREATED_DT - r.submitted_dt, 1) as "Time to hire"
FROM CANDAPPL_HISTORY_STAT_CHANGES ca
JOIN ADVERTISEMENTS a on A.AD_ID = CA.AD_ID
JOIN USERS u on a.user_id = U.USER_PK_ID
JOIN REPLIES r on CA.CAND_ID = R.CAND_ID and CA.AD_ID = R.AD_ID
WHERE ca.acc_id = '150500'
AND new_status = 'H'
AND old_status != 'H'
AND u.USER_PK_ID = '504407'
AND (r.submitted_dt is not null or r.referrer like '%(ATSi)%')
AND ca.CREATED_DT >= TO_DATE('2020-08-12', 'YYYY-MM-DD')
AND ca.CREATED_DT <= TO_DATE('2020-08-19', 'YYYY-MM-DD')
Is it possible to restructure this query to group by user_id and work out the average time to hire across all ad's per user?
I have the following that groups by user_id and works out the number of roles filled by user_id
SELECT u.USER_PK_ID, u.user_id, count(ca.ad_id) as "Roles Filled"
FROM CANDAPPL_HISTORY_STAT_CHANGES ca
JOIN ADVERTISEMENTS a on A.AD_ID = CA.AD_ID
JOIN USERS u on a.user_id = U.USER_PK_ID
WHERE ca.acc_id = '150500'
AND new_status = 'H'
AND old_status != 'H'
AND ca.CREATED_DT >= TO_DATE('2020-08-12', 'YYYY-MM-DD')
AND ca.CREATED_DT <= TO_DATE('2020-08-19', 'YYYY-MM-DD')
GROUP BY u.USER_PK_ID,u.user_id
ORDER BY count(ca.ad_id) DESC;
If my understanding of the problem is correct, you want the average time of hire per user id. That can be done by doing a groupby on the userid, something like below
SELECT u.USER_PK_ID, u.user_id, round(AVG(ca.CREATED_DT - r.submitted_dt, 1)) as "AVG Time to hire"
FROM CANDAPPL_HISTORY_STAT_CHANGES ca
JOIN ADVERTISEMENTS a on A.AD_ID = CA.AD_ID
JOIN USERS u on a.user_id = U.USER_PK_ID
JOIN REPLIES r on CA.CAND_ID = R.CAND_ID and CA.AD_ID = R.AD_ID
WHERE ca.acc_id = '150500'
AND new_status = 'H'
AND old_status != 'H'
AND (r.submitted_dt is not null or r.referrer like '%(ATSi)%')
AND ca.CREATED_DT >= TO_DATE('2020-08-12', 'YYYY-MM-DD')
AND ca.CREATED_DT <= TO_DATE('2020-08-19', 'YYYY-MM-DD')
GROUP BY u.USER_PK_ID, u.user_id

sql join not taking all records from another table

I have a query like this
WITH CTE AS
(
SELECT
U.Name, U.Adluserid AS 'Empid',
MIN(CASE WHEN IOType = 0 THEN Edatetime END) AS 'IN',
MAX(CASE WHEN IOType = 1 THEN Edatetime END) AS 'out',
(CASE
WHEN MAX(E.Status) = 1 THEN 'AL'
WHEN MAX(E.Status) = 2 THEN 'SL'
ELSE 'L'
END) AS leave_status
FROM
Mx_ACSEventTrn
RIGHT JOIN
Mx_UserMst U ON Mx_ACSEventTrn.UsrRefcode = U.UserID
LEFT JOIN
Tbl_Zeo_Empstatus E ON Mx_ACSEventTrn.UsrRefcode = E.Emp_Id
WHERE
CAST(Edatetime AS DATE) BETWEEN '2019-11-03' AND '2019-11-03'
GROUP BY
U.Name, U.Adluserid
)
SELECT
[Name], [Empid], [IN], [OUT],
(CASE
WHEN CAST([IN] AS TIME) IS NULL THEN CAST(leave_status AS NVARCHAR(50))
WHEN CAST([IN] AS TIME) < CAST('08:15' AS TIME) THEN 'P'
ELSE 'L'
END) AS status
FROM
CTE
In my employee master table Mx_UserMst I have 67 employees. But here it is showing only a few employees those who are punched. I want to show all employees from employee master
I believe that the problem is his WHERE clause:
where cast(Edatetime as date) between '2019-11-03' and '2019-11-03'
Why not cast(Edatetime as date) = '2019-11-03'?
I'm not sure in which table the column Edatetime belongs (you should qualify all the columns with the correct table name/alias).
You must move the condition to an ON clause:
WITH CTE AS
(
select U.Name,U.Adluserid as 'Empid',
min(case when IOType=0 then Edatetime end) as 'IN',
max(case when IOType=1 then Edatetime end) as 'out',
case max(E.Status) when 1 then 'AL' when 2 then 'SL' else 'L' end as leave_status
from Mx_UserMst U
left join Mx_ACSEventTrn on Mx_ACSEventTrn.UsrRefcode=U.UserID and (cast(Edatetime as date) between '2019-11-03' and '2019-11-03')
left join Tbl_Zeo_Empstatus E on Mx_ACSEventTrn.UsrRefcode=E.Emp_Id
group by U.Name,U.Adluserid
)
SELECT [Name], [Empid],[IN],[OUT],
case
when cast([IN] as time) is null then cast(leave_status as nvarchar(50))
when cast([IN] as time) < cast('08:15' as time) then 'P'
else 'L'
end as status
FROM CTE
If Edatetime belongs to Tbl_Zeo_Empstatus move the condition to the next join's ON clause.
I also changed the RIGHT to a LEFT join so to make the statement more readable.
If you want to keep everything in a particular table, then that should be the first table in the FROM clause. Subsequent joins should be LEFT JOINs and conditions on subsequent tables should be in the ON clause rather than the WHERE clause.
I would also advise you to use table aliases and to only use single quotes for string and date constants -- NOT column aliases.
The following assumes that IOType and Edatetime are in the table Mx_ACSEventTrn. I should not have to guess. You should qualify all column names in the query.
WITH CTE AS (
SELECT U.Name, U.Adluserid AS Empid,
MIN(CASE WHEN AE.IOType = 0 THEN AE.Edatetime END) AS in_dt,
MAX(CASE WHEN AE.IOType = 1 THEN AE.Edatetime END) AS out_dt,
(CASE WHEN MAX(ES.Status) = 1 THEN 'AL'
WHEN MAX(ES.Status) = 2 THEN 'SL'
ELSE 'L'
END) AS leave_status
FROM Mx_UserMst U LEFT JOIN
Mx_ACSEventTrn AE
ON AE.UsrRefcode = U.UserID AND
CAST(AE.Edatetime AS DATE) BETWEEN '2019-11-03' AND '2019-11-03' LEFT JOIN
Tbl_Zeo_Empstatus ES
ON AE.UsrRefcode = ES.Emp_Id AND
GROUP BY U.Name, U.Adluserid
)
SELECT Name, Empid, IN_DT, OUT_DT,
(CASE WHEN IN_DT IS NULL THEN leave_status
WHEN CAST(IN_DT AS TIME) < CAST('08:15' AS TIME) THEN 'P'
ELSE 'L'
END) AS status
FROM CTE;
Some more points:
Don't name aliases things like IN that are already key words. That is why I gave it the name IN_DT.
There is no reason to cast to a TIME to compare to NULL.
I don't see a reason to cast to NVARCHAR(50) in the outer CASE expression.

Counting records on multiple conditions

My query is to count the number of transactions that are happened in the tables. Below is my query:
select tt.transaction_key, tt.description, t.mode, count(t.TransactionType) as Frequency
from transaction_names tt left join
transaction_report_data t
on tt.transaction_key = t.TransactionType and
t.created >='2017-04-11' and t.created <= '2018-04-13'
group by tt.transaction_key, tt.description, t.mode;
I also need the count/frequency for the number of transactions which are successful only or which has t.status = 'success'.
Please help how can I adjust this 'where' clause to count this type of records also.
Thanks in advance.
You can do a case with aggregate like below
select
tt.transaction_key,
tt.description,
t.mode,
count(t.TransactionType) as Frequency,
Sum(case when t.status='success' then 1 else 0 end) as SuccessfulTransactions
from transaction_names tt left join
transaction_report_data t
on tt.transaction_key = t.TransactionType and
t.created >='2017-04-11' and t.created <= '2018-04-13'
group by tt.transaction_key, tt.description, t.mode;
Try this
SELECT
tt.transaction_key,
tt.[Description],
t.mode,
COUNT(t.TransactionType) AS Frequency,
Success = SUM(CASE WHEN T.[Status]='Success' THEN 1 ELSE 0 END)
FROM transaction_names tt
LEFT JOIN transaction_report_data t
ON tt.transaction_key = t.TransactionType
AND t.created >= '2017-04-11'
AND t.created <= '2018-04-13'
GROUP BY
tt.transaction_key,
tt.[Description],
t.mode;
select tt.transaction_key, tt.description, t.mode, count(t.TransactionType) as Frequency
from transaction_names tt left join
transaction_report_data t
on tt.transaction_key = t.TransactionType and
t.created >='2017-04-11' and t.created <= '2018-04-13'
group by tt.transaction_key, tt.description, t.mode, t.status
having t.status = 'success'

Can a Union have another column name as long as the number of columns is the same?

I have a query I would like to eventually put into a view.
First can I name my Flag column differently as long as I have 2 columns throughout?
Also, can I perform a union on the same table with a different WHERE clause?
Finally, what is the correct approach for getting these 3 selects into 1 view?
SELECT ID, 'Y' AS PW_CHANGE31, NULL AS LAST_LOGON90, NULL AS LAST_LOGON365 FROM user_info WITH (nolock) WHERE ([Last Password Change] < GETDATE() - 31)
UNION ALL
SELECT ID, NULL AS PW_CHANGE31, 'Y' AS LAST_LOGON90, NULL AS LAST_LOGON365 FROM user_info WITH (nolock) WHERE ([Last Logon] < GETDATE() - 90) AND ([Account Disabled] = 0)
UNION ALL
SELECT ID, NULL AS PW_CHANGE31, NULL AS LAST_LOGON90, 'Y' AS LAST_LOGON365 FROM user_info WITH (nolock) WHERE ([Last Logon] < GETDATE() - 365)
**UPDATED TO:
SELECT ID, '31' AS StatusVal FROM user_info WITH (nolock) WHERE ([Last Password Change] < GETDATE() - 31)
UNION
SELECT ID, '90' AS StatusVal FROM user_info WITH (nolock) WHERE ([Last Logon] < GETDATE() - 90) AND ([Account Disabled] = 0)
UNION
SELECT ID, '365' AS StatusVal FROM user_info WITH (nolock) WHERE ([Last Logon] < GETDATE() - 365)
I just realized what you are doing with this query. I think instead of having 3 rows for the same person (which is a potential result) with the UNION approach, you probably want 1 row per user with the 3 flags set with the proper values. Note: I did not run this query, there may be syntax errors.
SELECT u.ID,
CASE WHEN ISNULL(u1.id) THEN 'N' ELSE 'Y' END CASE AS PW_CHANGE31,
CASE WHEN ISNULL(u2.id) THEN 'N' ELSE 'Y' END CASE AS LAST_LOGON90,
CASE WHEN ISNULL(u3.id) THEN 'N' ELSE 'Y' END CASE AS LAST_LOGON365
FROM user_info u
LEFT OUTER JOIN user_info u1 on u.id = u1.id
AND ([Last Password Change] < GETDATE() - 31)
LEFT OUTER JOIN user_info u2 on u.id = u2.id
AND ([Last Logon] < GETDATE() - 90) AND ([Account Disabled] = 0)
LEFT OUTER JOIN user_info u3 on u.id = u3.id
AND ([Last Logon] < GETDATE() - 365)
WHERE u1.id IS NOT NULL
OR u2.id IS NOT NULL
OR u3.id IS NOT NULL
I tried to keep the same three subqueries in tact above so you could see the structure. I think you should eliminate the joins altogether and just do the arithmetic in the CASE statements like this:
SELECT u.ID,
CASE WHEN ([Last Password Change] < GETDATE() - 31) THEN 'Y' ELSE 'N' END CASE AS PW_CHANGE31,
CASE WHEN (([Last Logon] < GETDATE() - 90) AND ([Account Disabled] = 0)) THEN 'Y' ELSE 'N' END CASE AS LAST_LOGON90,
CASE WHEN ([Last Logon] < GETDATE() - 365) THEN 'Y' ELSE 'N' END CASE AS LAST_LOGON365
FROM user_info u
WHERE ([Last Password Change] < GETDATE() - 31)
OR (([Last Logon] < GETDATE() - 90) AND ([Account Disabled] = 0))
OR ([Last Logon] < GETDATE() - 365)
Again, these queries may have syntax errors, but I think gets you a better performing query along with the fact that you will only have one row per user with flags set correctly. Good luck!
That will work as long as all three columns are type compatible. The name only matters in so far as the result column will be called PW_CHANGE31 which may end up being confusing. In general I'd alias it .

Left Join is not working

I wrote following query
SELECT
us.Id as Id, us.Name as Name,
SUM(CASE WHEN c.isPublish = 0 THEN 1 ELSE 0 END) AS PendingCoupons,
SUM(CASE WHEN c.isPublish = 1 and convert(date,c.PublishedDate,101) >= convert(date, GETDATE(), 101) THEN 1 ELSE 0 END) AS ApprovedCouponsToday,
SUM(CASE WHEN c.isPublish = 0 and convert(date,c. CreateDate, 101) = convert(date, GETDATE(), 101) THEN 1 ELSE 0 END) AS PendingCouponsToday,
SUM(CASE WHEN c.isPublish = 1 THEN 1 ELSE 0 END) AS ApprovedCoupons,
SUM(CASE WHEN c.isPublish = 1 and c.Userid = us.Id and convert(date, c.PublishedDate, 101) >= convert(date, GETDATE(), 101) THEN 1 ELSE 0 END) AS ApprovedByUserToday,
SUM(CASE WHEN c.isPublish = 1 and c.Userid = us.Id THEN 1 ELSE 0 END) AS ApprovedByUser,
SUM(CASE WHEN c.ReviewVerify = 1 and convert(date, c.PublishedDate, 101) >= convert(date, GETDATE(), 101) THEN 1 ELSE 0 END) AS ProcessToday,
COUNT(*) AS Total
FROM
Users AS us
LEFT JOIN
Coupon c ON Userid = us.Id
GROUP BY
us.Name , us.Id
and I have following two tables
and after running above query the result is always this
Is there any error in this query , because its always returning me count " 0 " and I have almost 100 coupons on every user but its not showing
It would seem that since RIGHT JOIN works and gives correct answers (and probably a bogus user id) while a LEFT JOIN doesn't give any results related to coupons at all, the coupons are registered on a non existing user.
LEFT JOIN demands that data exists in the left part of the table that possibly exists in the right side, while RIGHT JOIN does the reverse. In other words, data exists in your rightmost table (coupon) that doesn't have any connection to the left side (user)
I think you may need to use a RIGHT JOIN to get all records form the Coupon table.
You not using the correct join.
You want to get coupon for a user id?
Use this join.
FROM
Users AS us
LEFT JOIN
Coupon c ON us.Id = c.Userid
if this does not work then use:
FROM
Users AS us
LEFT OUTER JOIN
Coupon c ON us.Id = c.Userid