Get Data Of Third Column Based on Two other Columns - sql

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;

Related

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

Get COUNT with a condition from a joined table

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

SQL check if column contains specific values

I have a table like this:
id | Values
------------------
1 | a
1 | b
1 | c
1 | d
1 | e
2 | a
2 | a
2 | c
2 | c
2 | e
3 | a
3 | c
3 | b
3 | d
Now I want to know which id contains at least one of a, one of b and one of c.
This is the result I want:
id
--------
1
3
One method is aggregation with having:
select id
from t
where values in ('a', 'b', 'c')
group by id
having count(distinct values) = 3;
If you wanted more flexibility with the counts of each value:
having sum(case when values = 'a' then 1 else 0 end) >= 1 and
sum(case when values = 'b' then 1 else 0 end) >= 1 and
sum(case when values = 'c' then 1 else 0 end) >= 1
You can use grouping:
SELECT id
FROM your_table
GROUP BY id
HAVING SUM(CASE WHEN value = 'a' THEN 1 ELSE 0 END) >= 1
AND SUM(CASE WHEN value = 'b' THEN 1 ELSE 0 END) = 1
AND SUM(CASE WHEN value = 'c' THEN 1 ELSE 0 END) = 1;
or using COUNT:
SELECT id
FROM your_table
GROUP BY id
HAVING COUNT(CASE WHEN value = 'a' THEN 1 END) >= 1
AND COUNT(CASE WHEN value = 'b' THEN 1 END) = 1
AND COUNT(CASE WHEN value = 'c' THEN 1 END) = 1;

how to get value x without code duplication

create table t(a int, b int);
insert into t values (1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3);
select * from t;
a | b
----------
1 | 1
1 | 2
1 | 3
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
3 | 3
select
max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c,
(
max(case when a = 1 then b else 0 end)
+
max(case when b = 1 then a else 0 end)
) as x
from t
Is it possible to do something like this?
select
max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c,
(q + c) as x
from t
You can't use the ALIAS that was given on the same level of the SELECT clause.
You have two choices:
by using the expression directly
query:
select
max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c,
(max(case when a = 1 then b else 0 end) + max(case when b = 1 then a else 0 end)) as x
from t
by wrapping in a subquery
query:
SELECT q,
c,
q + c as x
FROM
(
select
max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c
from t
) d
Also in SQLServer2005+ you can use CTE
;WITH cte AS
(
select max(case when a = 1 then b else 0 end) as q,
max(case when b = 1 then a else 0 end) as c
from t
)
SELECT q, c, q + c as x
FROM cte
You can't do that unfortunately.
The ALIAS can not be used in the same level where you created them.
A temporary table is necessary, i think.