Get COUNT with a condition from a joined table - sql

I have a table SyncHistory:
SyncHistoryId SyncType SyncDateTime
-----------------------------------------------------
55 1 2017-11-28 09:30:51.810
56 1 2017-11-28 10:30:32.123
And then another table SyncDetails:
SyndDetailId SyncHistoryId ItemId ItemCreated ItemChanged
---------------------------------------------------------------------------
98 55 12345 1 0
99 55 23183 1 0
100 55 87687 0 1
101 55 23234 0 0
102 55 23222 0 0
103 56 9928 1 0
What I'm trying to do is create a query that gives me this:
Sync Data New Existing & Changed Existing & Not Changed
---------------------------------------------------------------------------
11/28/2017 9:30am 2 1 2
11/28/2017 10:30am 1 0 0
This is what I'm trying:
SELECT
sh.SyncHistoryId
, sh.SyncDateTime
, count(sd1.SyncDetailId) AS Created
, count(sd2.SyncDetailId) AS ExistingChanged
, count(sd3.SyncDetailId) AS ExistingNotChanged
FROM
SyncHistory sh
LEFT JOIN SyncDetails sd1 ON sh.SyncHistoryId = sd1.SyncHistoryId AND sd1.ItemCreated = 1 AND sd1.ItemChanged = 0
LEFT JOIN SyncDetails sd2 ON sh.SyncHistoryId = sd2.SyncHistoryId AND sd2.ItemCreated = 0 AND sd2.ItemChanged = 1
LEFT JOIN SyncDetails sd3 ON sh.SyncHistoryId = sd3.SyncHistoryId AND sd3.ItemCreated = 0 AND sd3.ItemChanged = 0
WHERE
sh.SyncType = 1
GROUP BY
sh.SyncHistoryId
, sh.SyncDateTime
ORDER BY
sh.SyncDateTime DESC
But, none of the resulting counts are accurate. I'm doing something wrong, but not sure what.

SELECT h.SyncDateTime,
SUM(case when d.ItemCreated = 1 then 1 else 0 end) as New,
SUM(case when d.ItemChanged = 1 then 1 else 0 end) as [Existing & Changed],
SUM(case when d.ItemCreated = 0 and d.ItemChanged = 0 then 1 else 0 end) as [Existing & Not Changed]
FROM SyncHistory h
INNER JOIN SyncDetails d ON h.SyncHistoryId = d.SyncHistoryId
GROUP BY h.SyncDateTime

You only need to JOIN to the details table once. You can get your counts from that through aggregation:
SELECT
CONVERT(VARCHAR(16), SH.SyncDateTime, 120) AS SyncTime,
SUM(CASE WHEN SD.ItemCreated = 1 AND SD.ItemChanged = 0 THEN 1 ELSE 0 END) AS New,
SUM(CASE WHEN SD.ItemCreated = 0 AND SD.ItemChanged = 1 THEN 1 ELSE 0 END) AS ExistingAndChanged,
SUM(CASE WHEN SD.ItemCreated = 0 AND SD.ItemChanged = 0 THEN 1 ELSE 0 END) AS ExistingAndNotChanged
FROM
SyncHistory SH
LEFT OUTER JOIN SyncDetails SD ON SD.SyncHistoryID = SH.SyncHistoryID
GROUP BY
CONVERT(VARCHAR(16), SH.SyncDateTime, 120)
You weren't clear on how the grouping/datetime should be determined. What I have is by the minute. If it's supposed to be by the hour on the 1/2 hour mark or something else then you'll need to change that part of the query in the GROUP BY and the first column of the SELECT.

Another solution. I hope it will work - no CASE, no subquery:
SELECT
sh.SyncHistoryId
,sh.SyncDateTime
,COUNT( NULLIF( sd.ItemCreated, 0 ) ) AS Created
,COUNT( NULLIF( sd.ItemCreated, 1 ) + NULLIF( sd1.ItemChanged, 0 ) ) AS ExistingChanged
,COUNT( NULLIF( sd.ItemCreated, 1 ) + NULLIF( sd1.ItemChanged, 1 ) ) AS ExistingNotChanged
FROM
SyncHistory sh JOIN SyncDetails sd ON sh.SyncHistoryId = sd.SyncHistoryId
WHERE
sh.SyncType = 1
GROUP BY
sh.SyncHistoryId
,sh.SyncDateTime
ORDER BY
sh.SyncDateTime DESC

I hope subquery is not forbidden:
SELECT
sh.SyncHistoryId
,sh.SyncDateTime
,(SELECT COUNT(*) FROM SyncDetails sd WHERE sh.SyncHistoryId = sd.SyncHistoryId AND sd.ItemCreated = 1 AND sd1.ItemChanged = 0) AS Created
,(SELECT COUNT(*) FROM SyncDetails sd WHERE sh.SyncHistoryId = sd.SyncHistoryId AND sd.ItemCreated = 0 AND sd1.ItemChanged = 1) AS ExistingChanged
,(SELECT COUNT(*) FROM SyncDetails sd WHERE sh.SyncHistoryId = sd.SyncHistoryId AND sd.ItemCreated = 0 AND sd1.ItemChanged = 0) AS ExistingNotChanged
FROM
SyncHistory sh
WHERE
sh.SyncType = 1
ORDER BY
sh.SyncDateTime DESC

Related

Inner Join Taking Forever To Run In Big Query

I have a query that is taking forever to do an inner join. It runs for 6 hours and then times out. The query is:
SELECT
a.*,
b.total_video,
b.total_initial_video,
b.total_discovery,
b.total_sub_video,
b.total_sub_initial_video,
b.total_sub_discovery,
FROM temp_table.source_stage_ a
INNER JOIN temp_table.source_stage2_ b
ON (a.day = b.day
AND a.state_desc = b.state_desc
AND a.package_desc = b.package_desc
AND a.brand = b.v1_brand
))
temp_table.source_stage_ is
create or replace table temp_table.source_stage_ as (
select *,
case
when cycle = 0
then 'Trial'
when cycle = 1
then '1 Month'
when cycle between 2 and 3
then '2 - 3 Months'
when cycle between 4 and 5
then '4 - 5 Months'
when cycle >= 6
then '6+ Months'
end as group,
from(
select
*,
case when active_days < 0 then 0 else cast( ceil( ( case when active_days < 1 then 1 else active_days end ) / 30 ) as int64 ) end cycle, from (
SELECT
t.activation_dt,
t.end_dt,
t.trial_start_dt,
t.start_dt,
date_diff(p.day_dt,coalesce(activation_dt,trial_start_dt,start_dt),DAY) as active_days,
date_diff(p.day_dt,coalesce(start_dt),DAY) as active_days,
p.day_dt,
state_desc,
package_desc,
v1_brand,
sc.scarp_group,
p.last_touch,
SUM(p.video_playback_ind) as video_start_cnt,
SUM(CASE WHEN cd_path_rank_nbr = 1 THEN p.video_playback_ind ELSE 0 END) as initial_video_start_cnt,
SUM(CASE WHEN p.video_playback_ind = 1 AND f.session_nbr IS NOT NULL THEN 1 ELSE 0 END) as discovery_start_cnt,
COUNT(DISTINCT(p.registration_id_nbr)) as sub_video_start_cnt,
COUNT(DISTINCT(CASE WHEN cd_path_rank_nbr = 1 THEN p.registration_id_nbr ELSE NULL END)) as sub_initial_video_start_cnt,
COUNT(DISTINCT(CASE WHEN f.session_nbr IS NOT NULL THEN p.registration_id_nbr ELSE NULL END)) as sub_discovery_start_cnt,
FROM table1 p
LEFT JOIN table2 f
ON p.day_dt = f.day_dt
AND p.session_nbr = f.session_nbr
AND f.day_dt = '2023-02-11'
LEFT JOIN table3 t
on (p.registration_id_nbr = t.user_id_cd
and p.day_dt between t.activation_dt and t.end_dt)
left join table4 as sc
on (p.registration_id_nbr is not null
and sc.user_id_cd is not null
and p.registration_id_nbr = sc.user_id_cd
and p.day_dt between sc.period_start_dt and sc.period_end_dt)
WHERE p.day_dt = '2023-02-11'
AND p.video_playback_ind = 1
AND p.report_suite_id NOT IN ('cb', 'ic', 'bs')
AND LOWER(p.state_desc) IN ("tr", "s", "di of")
AND lower(package_desc) IN ('cf', 'lc', 'lcp', 'cf-s', 'lc-s', 'lcp-s')
AND v1_brand LIKE 'pp_%'
GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12
)))
temp_table.source_stage2_ is
create or replace table temp_table.source_stage2 as (
select *,
case
when tenure_cycle = 0
then 'Trial'
when tenure_cycle = 1
then '1 Month'
when tenure_cycle between 2 and 3
then '2 - 3 Months'
when tenure_cycle between 4 and 5
then '4 - 5 Months'
when tenure_cycle >= 6
then '6+ Months'
end as tenure_group,
from(
select
*,
case when paid_active_days < 0 then 0 else cast( ceil( ( case when paid_active_days < 1 then 1 else paid_active_days end ) / 30 ) as int64 ) end tenure_cycle, from (
SELECT
t.activation_dt,
t.end_dt,
t.trial_start_dt,
t.paid_start_dt,
date_diff(p.day_dt,coalesce(activation_dt,trial_start_dt,paid_start_dt),DAY) as active_days,
date_diff(p.day_dt,coalesce(paid_start_dt),DAY) as paid_active_days,
p.day_dt,
state_desc,
package_desc,
v1_brand_nm,
sc.scarp_group,
SUM(p.video_playback_ind) as total_video,
SUM(CASE WHEN cd_path_rank_nbr = 1 THEN p.video_playback_ind ELSE 0 END) as total_initial_video,
SUM(CASE WHEN p.video_playback_ind = 1 AND f.v88_player_session_nbr IS NOT NULL THEN 1 ELSE 0 END) as total_discovery,
COUNT(DISTINCT(p.v69_registration_id_nbr)) as total_sub_video,
COUNT(DISTINCT(CASE WHEN cd_path_rank_nbr = 1 THEN p.v69_registration_id_nbr ELSE NULL END)) as total_sub_initial_video,
COUNT(DISTINCT(CASE WHEN f.v88_player_session_nbr IS NOT NULL THEN p.v69_registration_id_nbr ELSE NULL END)) as total_sub_discovery,
FROM `table1` p
LEFT JOIN `table2` f
ON p.day_dt = f.day_dt
AND p.v88_player_session_nbr = f.v88_player_session_nbr
AND f.day_dt = '2023-02-11'
LEFT JOIN `table3` t
on (p.registration_id_nbr = t.cbs_reg_user_id_cd
and p.day_dt between t.activation_dt and t.end_dt)
left join `table4` as sc
on (p.registration_id_nbr is not null
and reg_user_id_cd is not null
and p.registration_id_nbr = reg_user_id_cd
and p.day_dt between sc.period_start_dt and sc.period_end_dt)
WHERE p.day_dt = '2023-02-11'
AND p.video_playback_ind = 1
AND p.report_suite_id NOT IN ('cb', 'ic', 'bs')
AND LOWER(p.state_desc) IN ("tr", "s", "di of")
AND lower(package_desc) IN ('cf', 'lc', 'lcp', 'cf-s', 'lc-s', 'lcp-s')
AND v1_brand_nm LIKE 'pp_%'
GROUP BY 1,2,3,4,5,6,7,8,9,10,11
)))
The last two queries, temp_table.source_stage_ and temp_table.source_stage2_ create temp tables and are the same except one column that is added to the first one

How to Replace NULL Value with 0 (Zero)?

I've just got myself stuck with some SQL query and I'm quite new on this.
I'm using pivot in my query.
This is my SELECT query:
SELECT *
FROM
(SELECT lg.domainNameID AS [Domain ID], COUNT(lg.domainNameID) AS [Fix Count]
FROM tbl_ATT_Request r
INNER JOIN tbl_ATT_Login lg ON lg.workdayID = r.workdayID
WHERE r.requestCategoryID = 1
GROUP BY lg.domainNameID) slct
and this is the output:
Domain | Fix Count
-------+-----------
1 1
2 1
4 2
5 1
And this is my query with PIVOT.
SELECT *
FROM
(SELECT lg.domainNameID AS [Domain ID], COUNT(lg.domainNameID) AS [Fix Count]
FROM tbl_ATT_Request r
INNER JOIN tbl_ATT_Login lg ON lg.workdayID = r.workdayID
WHERE r.requestCategoryID = 1
GROUP BY lg.domainNameID) slct
PIVOT
(SUM(slct.[Fix Count])
FOR slct.[Domain ID] IN ([1],[2],[3],[4],[5])
) AS pvt
This is the output:
1 | 2 | 3 | 4 | 5
1 1 NULL 2 1
Now my problem is how can I replace the NULL values with 0.
Just use conditional aggregation:
SELECT SUM(CASE WHEN Domain_Id = 1 THEN Fix_Count ELSE 0 END) as d_1,
SUM(CASE WHEN Domain_Id = 2 THEN Fix_Count ELSE 0 END) as d_2,
SUM(CASE WHEN Domain_Id = 3 THEN Fix_Count ELSE 0 END) as d_3,
SUM(CASE WHEN Domain_Id = 4 THEN Fix_Count ELSE 0 END) as d_4,
SUM(CASE WHEN Domain_Id = 5 THEN Fix_Count ELSE 0 END) as d_5
FROM (SELECT lg.domainNameID AS Domain_ID, COUNT(*) AS Fix_Count
FROM tbl_ATT_Request r JOIN
tbl_ATT_Login lg
ON lg.workdayID = r.workdayID
WHERE r.requestCategoryID = 1
GROUP BY lg.domainNameID
) d

SQL Select with joins and calculate percentage

I have three tables and I'm trying to make a select statement to give me a result like the one below
Teams:
ID Name
1 A
2 B
3 C
Players:
ID Name TeamID
1 P1 1
2 P2 1
3 P3 2
Goals: (goaltype: H for home, A for away, T for training)
ID PID goaltype
1 1 A
2 1 A
3 1 H
4 2 A
5 2 H
6 3 A
7 3 T
Result will be Like:
Team totalGoals home away trainig percentage[(home/total)*100]
A 5 2 3 0 40%
B 2 0 1 1 0
C 0 0 0 0 0
This is my current query:
select t.name,
count(g.id) as totalGoals,
sum(case when g.GTYPE = 'H' then 1 else 0 end) as home,
sum(case when g.GTYPE = 'A' then 1 else 0 end) as away,
sum(case when g.GTYPE = 'T' then 1 else 0 end) as training,
--(home/totalGoals) as percentage
from teams t
left join players p on p.TeamID = t.id
left join goals g on g.pid = p.id
group by t.name
You can use conditional aggregation to get the results you want:
SELECT t.Name AS Team,
COUNT(g.goaltype) AS totalGoals,
SUM(CASE WHEN g.goaltype = 'H' THEN 1 ELSE 0 END) AS home,
SUM(CASE WHEN g.goaltype = 'A' THEN 1 ELSE 0 END) AS away,
SUM(CASE WHEN g.goaltype = 'T' THEN 1 ELSE 0 END) AS training,
CASE WHEN COUNT(g.goaltype) = 0 THEN 0
ELSE 100.0 * SUM(CASE WHEN g.goaltype = 'H' THEN 1 ELSE 0 END) /
COUNT(g.goaltype)
END AS percentage
FROM Teams t
LEFT JOIN Players p ON p.TeamID = t.ID
LEFT JOIN Goals g ON g.PID = p.ID
GROUP BY t.Name
ORDER BY t.Name
Output:
team totalgoals home away training percentage
A 5 2 3 0 40
B 2 0 1 1 0
C 0 0 0 0 0
Demo on SQLFiddle

Fetch the total from the result of a query

The query i am using is
select convert(varchar(10), sales.saledate, 103) [SaleDate], SUM(sales.Domestic) [Domestic], SUM(sales.Export) [Export], SUM(sales.Import) [Import],
SUM(sales.Value) [Value], Sum(sales.Cancelled) [Cancelled], sum(sales.cancelledValue) [CancelledValue], SUM(sales.totalValue) [TotalValue]
from
(
select max(j.SaleDate) SaleDate,
case when max(oc.Code) = 'AU' and max(dc.Code) = 'AU' then 1 else 0 end [Domestic],
case when max(oc.Code) = 'AU' and max(dc.Code) <> 'AU' then 1 else 0 end [Export],
case when max(oc.Code) <> 'AU' and max(dc.Code) = 'AU' then 1 else 0 end [Import],
1 [Total],
max(ic.Total) [Value],
case when max(c.CancelDate) is not null then 1 else 0 end [Cancelled],
case when max(c.CancelDate) is not null then max(ic.Total) else 0 end [CancelledValue],
case when max(c.CancelDate) is null then max(ic.Total) else 0 end [TotalValue]
from invoices i
left join Jobs j on i.JobKey = j.JobKey
inner join tasks t on j.jobkey = t.jobkey
inner join Consignments c on t.TaskKey = c.consignmentkey
inner join places op on c.originplacekey = op.placekey
inner join places dp on c.destinationplacekey = dp.placekey
inner join places oC on dbo.ParentPlaceKey(c.originPlaceKey) = oc.placekey
inner join places dC on dbo.ParentPlaceKey(c.destinationplacekey) = dc.placekey
left join (select consignmentKey, sum(Value) [Value] from ConsignmentItems ci group by consignmentkey ) ci on ci.ConsignmentKey = c.ConsignmentKey
left join (select invoicekey, sum(case when ci.ChargeItemKey = 'FRT_SLL' then oc.Value else 0 end) [Freight],
sum(case when ci.ChargeItemKey = 'WTY_SLL' then oc.Value else 0 end) [Warranty],
sum(case when ci.ChargeType = 4 then oc.Value else 0 end) [Total] from InvoiceCharges ic
left join OptionCharges oc on ic.OptionChargeKey = oc.OptionChargeKey
left join ChargeItems ci on oc.ChargeItemKey = ci.ChargeItemKey
group by invoicekey
) ic on ic.InvoiceKey = i.InvoiceKey
where
j.SaleDate >= '01-Apr-2013' and j.SaleDate <= '10-May-2013'
and
j.operationalstorekey = dbo.StoreCode('AU-WEB')
and j.saledate is not null and SelectedOptionKey is not null
group by j.jobkey
) sales
group by convert(varchar(10), sales.saledate, 103)
order by max(sales.saledate)
The result of a sql query is
SaleDate Domestic Export Import Value Cancelled CancelledValue Totalvalue
11/04/2013 1 0 0 47.200 0 0.0000 47.2000
16/04/2013 6 0 0 249.750 0 0.0000 249.7500
22/04/2013 0 1 0 223.480 0 0.0000 223.4800
23/04/2013 0 3 0 670.440 0 0.0000 670.4400
I want result like (want to add the TOTALS at the end)
SaleDate Domestic Export Import Value Cancelled CancelledValue Totalvalue
11/04/2013 1 0 0 47.200 0 0.0000 47.2000
16/04/2013 6 0 0 249.750 0 0.0000 249.7500
22/04/2013 0 1 0 223.480 0 0.0000 223.4800
23/04/2013 0 3 0 670.440 0 0.0000 670.4400
TOTALS 7 4 0 1190.432 0 0 1190.432
Can anyone please tell me how to achieve this in above query, i am trying with temp tables which i dont want.
Thanks.
You can achieve this by adding an UNION at the end of your result. Something like:
... order by max(sales.saledate)
UNION
(SELECT "TOTALS", SUM(sales.Domestic) AS Domestic, SUM(sales.Export) AS Export,
SUM(sales.Import) AS Import, SUM(sales.Value) [Value], Sum(sales.Cancelled) AS Cancelled, sum(sales.cancelledValue) AS CancelledValue, SUM(sales.totalValue) AS TotalValue
FROM "your_big_query"...
WHERE ...
)
And this time remove the group by convert(varchar(10), sales.saledate, 103)
EDIT
Or you can use the GROUP BY Modifiers try to use this in the GROUP BY statement:
group by convert(varchar(10), sales.saledate, 103) WITH ROLLUP
You can read the official documentation here with some examples too.
Hope this works!!

Get Data Of Third Column Based on Two other Columns

This is a sample table
ID STOREA STOREB STOREC AB BC CA ABC
--- ------- ------ ------- -- -- --- ---
10 1 0 0
10 0 1 0
10 0 1 0
29 0 1 0
29 0 0 1
29 1 0 0
Each row corresponds to a purchase made at either of Store A or B or C. Customer 10 shops at A and B but not c. So I want AB=1 BC=0 CA=0 ABC=0 for all ID=10 rows and for ID=29, he shops at all 3, so I need AB=1 BC=1 CA=1 ABC=1 for all rows where ID=29 (using ORACLE SQL)
I would like to update the columns in the table.
Here is one way you can do this. I don't think you can use JOINs in Oracle with UPDATE statements -- however, you can accomplish the same thing by using MERGE:
MERGE
INTO yourtable
USING (
select id as idnew,
case when a + b = 2 then 1 else 0 end abnew,
case when b + c = 2 then 1 else 0 end bcnew,
case when a + c = 2 then 1 else 0 end acnew,
case when a + b + c = 3 then 1 else 0 end abcnew
from (
select
id,
max(case storea when 1 then 1 else 0 end) A,
max(case storeb when 1 then 1 else 0 end) B,
max(case storec when 1 then 1 else 0 end) C
from yourtable
group by id
) a
)
ON (id = idnew)
WHEN MATCHED THEN
UPDATE
SET ab = abnew,
bc = bcnew,
ac = acnew,
abc = abcnew
SQL Fiddle Demo
Here is how you can do this as a select:
update (select id, storea, storeb, storec, AB as new_AB, BC as new_BC, AC as new_AC, ABC as new_ABC
from t join
(select id,
(case when max(storeA) = 1 and max(storeB) = 1 then 1 else 0 end) as AB,
(case when max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as BC,
(case when max(storeA) = 1 and max(storeC) = 1 then 1 else 0 end) as AC,
(case when max(storeA) = 1 and max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as ABC
from t
group by id
) tsum
on t.id = tsum.id
)
set AB = new_AB, AC = new_AC, BC = new_BC, ABC = new_ABC;
I think this might work:
select id, storea, storeb, storec, AB, BC, AC, ABC
from t join
(select id,
(case when max(storeA) = 1 and max(storeB) = 1 then 1 else 0 end) as AB,
(case when max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as BC,
(case when max(storeA) = 1 and max(storeC) = 1 then 1 else 0 end) as AC,
(case when max(storeA) = 1 and max(storeB) = 1 and max(storeC) = 1 then 1 else 0 end) as ABC
from t
group by id
) tsum
on t.id = tsum.id
)
set AB = new_AB, AC = new_AC, BC = new_BC, ABC = new_ABC;