SQL count condition with 12months table - sql

I have the following table Named: LISTON
| TYPE | MONTHS | New | Old |
+------+--------+-----+-----+
| A | FEB | Y | N |
| A | MAY | Y | N |
| A | MAY | N | Y |
| B | MAY | Y | N |
| A | MAY | Y | N |
| C | MAY | Y | N |
| D | MAY | Y | N |
I would like to arrange the data above into the format shown below:
| MONTHS | A | B | C | D |
| | New | Old | New | Old | New | Old | New | Old |
+--------+-----+-----+-----+-----+-----+-----+-----+-----+
| JAN | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| FEB | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| MAR | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| APR | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| MAY | 2 | 1 | 1 | 0 | 1 | 0 | 1 | 0 |
Is it possible to do that in SQL? by count or by sorting? Or any other ways would be helpful.

You can try this code:
with
MyTable as
( select 'A' as type, 'FEB' as months, 'Y' as new, 'N' as old from dual
union all select 'A', 'MAY', 'Y', 'N' from dual
union all select 'A', 'MAY', 'N', 'Y' from dual
union all select 'B', 'MAY', 'Y', 'N' from dual
union all select 'A', 'MAY', 'Y', 'N' from dual
union all select 'C', 'MAY', 'Y', 'N' from dual
union all select 'D', 'MAY', 'Y', 'N' from dual
)
select *
from MyTable
pivot ( count(decode(new, 'Y', 1, null)) as new
, count(decode(old, 'Y', 1, null)) as old
for type in ('A' a, 'B' b, 'C' c, 'D' d)
)
Order by 1

you can do this with pivot, e.g.
with pvt as(select * from mtable
pivot (
sum(case new when 'Y' then 1 else 0 end) new,
sum(case old when 'Y' then 1 else 0 end) old
for atype in ('A' a, 'B' b, 'C' c, 'D' d)
)
order by to_char(to_date(months, 'MON'), 'mm')),
mnt as (select level mid, to_char(to_date(to_char(level,'09'),'mm'),'MON') months from dual connect by level <= 12)
select months, nvl(a_new,0) a_new, nvl(a_old,0) a_old,
nvl(b_new,0) b_new, nvl(b_old,0) b_old,
nvl(c_new,0) c_new, nvl(c_old,0) c_old,
nvl(d_new,0) d_new, nvl(d_old,0) d_old
from mnt
left outer join pvt using (months)
order by mnt.mid;
This gives output like:
MONTHS A_NEW A_OLD B_NEW B_OLD C_NEW C_OLD D_NEW D_OLD
------+-----+-----+-----+-----+-----+-----+-----+-----
JAN 0 1 0 0 1 0 0 0
FEB 0 1 0 0 0 0 0 1
MAR 0 0 0 0 1 0 1 0
APR 0 0 0 0 0 0 0 1
MAY 0 1 0 0 0 0 0 0
JUN 0 0 0 0 0 1 1 0
JUL 0 0 0 0 0 0 0 0
AUG 0 2 0 0 0 0 1 0
SEP 0 1 0 0 0 0 0 1
OCT 0 0 0 0 0 0 0 0
NOV 0 0 0 0 0 0 0 0
DEC 1 0 0 2 0 1 0 1

You can do this using SUM CASE WHEN conditions as given below.
SELECT MONTHS,
SUM(CASE WHEN A.TYPE = 'A' AND A.NEW = 'Y' THEN 1 ELSE 0 END) AS A_NEW,
SUM(CASE WHEN A.TYPE = 'A' AND A.OLD = 'Y' THEN 1 ELSE 0 END) AS A_OLD,
SUM(CASE WHEN A.TYPE = 'B' AND A.NEW = 'Y' THEN 1 ELSE 0 END) AS B_NEW,
SUM(CASE WHEN A.TYPE = 'B' AND A.OLD = 'Y' THEN 1 ELSE 0 END) AS B_OLD,
SUM(CASE WHEN A.TYPE = 'C' AND A.NEW = 'Y' THEN 1 ELSE 0 END) AS C_NEW,
SUM(CASE WHEN A.TYPE = 'C' AND A.OLD = 'Y' THEN 1 ELSE 0 END) AS C_OLD,
SUM(CASE WHEN A.TYPE = 'D' AND A.NEW = 'Y' THEN 1 ELSE 0 END) AS D_NEW,
SUM(CASE WHEN A.TYPE = 'D' AND A.OLD = 'Y' THEN 1 ELSE 0 END) AS D_OLD
FROM
MYTABLE A
GROUP BY MONTHS;

You can manage it by using pivot clause :
with tab as
(
select mon, type||new||old type from months m1 right outer join
( select to_char(to_date(lpad(level,2,'0'),'MM'),'MON') mon, level mon_nr from dual
connect by level <= 5 ) m2 on ( m1.month = m2.mon ) -- you can replace 5 with 12 for whole year
)
select * from tab
pivot
(
count(1) for (type) in ('AYN' as "A (New)",'ANY' as "A (Old)",'BYN' as "B (New)",'BNY' as "B (Old)",'CYN' as "C (New)",'CNY' as "C (Old)",'DYN' as "D (New)",'DNY' as "D (Old)")
)
order by to_date(mon,'MM');
where the months table is created as below :
create table months(Type varchar2(1),Month varchar2(3),New varchar2(1),Old varchar2(1));
and the output is:
MON A (New) A (Old) B (New) B (Old) C (New) C (Old) D (New) D (Old)
JAN 0 0 0 0 0 0 0 0
FEB 1 0 0 0 0 0 0 0
MAR 0 0 0 0 0 0 0 0
APR 0 0 0 0 0 0 0 0
MAY 2 1 1 0 1 0 1 0
D e m o

Since you tagged your post with plsql, I'm assuming you are using Oracle database.
From your description, it looks to me that you want to convert rows into columns. This can be done in Oracle using what's known as a pivot. This Web page shows you how to write a pivot query. Hope it helps.
Good Luck,
Avi.

Related

How to query all months from January to December

How to query all months from January to December for all values. The values of 0 should also be presented in query result.
My query:
SELECT *
FROM
(SELECT
MONTH(begin_ts) AS [Month],
SUM(CASE
WHEN bmktonr = '7'
THEN dauer
ELSE reserve1
END) + SUM(CASE
WHEN bmktonr = '11'
THEN dauer
ELSE reserve1
END) AS Prozess_Verfügbarkeit,
SUM(CASE
WHEN bmktonr = '1'
THEN dauer
ELSE reserve1
END) + SUM(CASE
WHEN bmktonr = '2'
THEN dauer
ELSE reserve1
END) AS Verfügbarkeit
FROM
[hydra1].[hydadm].[v_ereignis]
WHERE
masch_nr = 'FIMI1'
AND YEAR(begin_ts) = YEAR(CURRENT_TIMESTAMP)
GROUP BY
MONTH(begin_ts)) T
INNER JOIN
(SELECT
p.masch_nr,
SUM(b.ruest_zeit) AS SOLLRüsten,
SUM(b.bearb_zeit) AS SOLLProduktion,
SUM(b.ruest_zeit_zuschl) AS SOLLZuschlag,
SUM(p.bmk_07) AS ISTRüsten,
SUM(p.bmk_11) AS ISTProduktion,
MONTH(prot_dat) AS Month
FROM
[hydra1].[hydadm].[v_auftrag_status] p
JOIN
[hydra1].[hydadm].[v_auftrags_bestand] b ON b.auftrag_nr = p.auftrag_nr
WHERE
p.masch_nr = 'GEORG'
AND a_status = 'E'
AND YEAR(prot_dat) = YEAR(CURRENT_TIMESTAMP)
GROUP BY
p.masch_nr, MONTH(prot_dat)) T1 ON T.Month = T1.Month
ORDER BY
T.Month
This is how it should look then:
Month | Prozess_Verfügbarkeit | Verfügbarkeit
------+-----------------------+--------------
1 | 344 | 4556
2 | 0 | 0
3 | 0 | 0
4 | 0 | 0
5 | 0 | 0
6 | 0 | 0
7 | 0 | 0
8 | 0 | 0
9 | 0 | 0
10 | 0 | 0
11 | 0 | 0
12 | 0 | 0
Thanks a lot.
Something like this ought to do it:
WITH months AS (
SELECT 1 AS month_number
UNION ALL SELECT 2
UNION ALL SELECT 3
...
UNION ALL SELECT 12
)
SELECT months.month_number
, your_query.thing_a
, your_query.thing_b
FROM months
LEFT
JOIN your_query
ON your_query.Month = months.month_number
;

Parse column B content into logical parts according to column A

I have a table like this
sessionId | hostname
------ | ------
a1 | domain1
a1 | domain2
a2 | domain1
a3 | domain1
a3 | domain2
a4 | domain2
What I want is to build a logical table containing the follwoing
sessionId | only domain1 | only domain2 | domain1 OR domain2 | domain1 AND domain2
-----------|----------------|--------------|--------------------|--------------------
a1 | 1 | 1 | 1 | 1
a2 | 1 | 0 | 1 | 0
a3 | 1 | 1 | 1 | 1
a4 | 0 | 1 | 1 | 0
I guess there's a simple solution for this, but I can't get my head over it :(
You can use conditional aggregation:
select (case when sum(case when hostname = 'domain1' then 1 else 0 end) > 0
then 1 else 0
end) as domain1,
(case when sum(case when hostname = 'domain2' then 1 else 0 end) > 0
then 1 else 0
end) as domain2,
(case when sum(case when hostname = 'domain1' then 1 else 0 end) > 0 or
sum(case when hostname = 'domain2' then 1 else 0 end) > 0
then 1 else 0
end) as either,
(case when sum(case when hostname = 'domain1' then 1 else 0 end) > 0 and
sum(case when hostname = 'domain2' then 1 else 0 end) > 0
then 1 else 0
end) as both
from t
group by sessionid;
Try this :
Declare #Table as Table (sessionId varchar(100),hostname varchar(100))
Insert into #Table Values
('a1','domain1'),
('a1','domain2'),
('a2','domain1'),
('a3','domain1'),
('a3','domain2'),
('a4','domain2')
Select distinct T.sessionId,
case when s1.sessionid is null then 0 else 1 end [only domain1],
case when s2.sessionid is null then 0 else 1 end [only domain2],
case when
(
case when s1.sessionid is null then 0 else 1 end = 1 or
case when s2.sessionid is null then 0 else 1 end = 1
) then 1 else 0 end [domain1 OR domain2],
case when
(
case when s1.sessionid is null then 0 else 1 end = 1 and
case when s2.sessionid is null then 0 else 1 end = 1
) then 1 else 0 end [domain1 AND domain2]
from #Table T
Left Join
(
Select sessionId From #Table where hostname = 'domain1'
) s1 on s1.sessionId = T.sessionId
Left Join
(
Select sessionId From #Table where hostname = 'domain2'
) s2 on s2.sessionId = T.sessionId
For BigQuery Standard SQL
#standardSQL
SELECT
sessionId,
SIGN(COUNTIF(hostname='domain1')) only_domain1,
SIGN(COUNTIF(hostname='domain2')) only_domain2,
SIGN(COUNTIF(hostname='domain1')+COUNTIF(hostname='domain2')) domain1_or_domain2,
SIGN(COUNTIF(hostname='domain1')*COUNTIF(hostname='domain2')) domain1_and_domain2
FROM `yourproject.yourdataset.yourtable`
GROUP BY sessionId
you can test / play with it using dummy data from your question
#standardSQL
WITH `yourproject.yourdataset.yourtable` AS (
SELECT 'a1' sessionId, 'domain1' hostname UNION ALL
SELECT 'a1', 'domain2' UNION ALL
SELECT 'a2', 'domain1' UNION ALL
SELECT 'a3', 'domain1' UNION ALL
SELECT 'a3', 'domain2' UNION ALL
SELECT 'a4', 'domain2'
)
SELECT
sessionId,
SIGN(COUNTIF(hostname='domain1')) only_domain1,
SIGN(COUNTIF(hostname='domain2')) only_domain2,
SIGN(COUNTIF(hostname='domain1')+COUNTIF(hostname='domain2')) domain1_or_domain2,
SIGN(COUNTIF(hostname='domain1')*COUNTIF(hostname='domain2')) domain1_and_domain2
FROM `yourproject.yourdataset.yourtable`
GROUP BY sessionId
ORDER BY sessionId

Select As Status If Subtable Lines Include Value

I have 2 Table named Order and OrderDetails
Order Table
orderID orderDate OrderUserId
1 10 01.01.2010 .....
2 20 05.05.2011 .....
OrderDetails Table
DetailID OrderIdOfDetail DetailProductID DetailStatusID
1 25 10 xxx 1
2 26 10 xxx 2
3 27 10 xxx 2
4 28 10 xxx 0
5 29 10 xxx 1
6 30 20 xxx 1
7 31 20 xxx 2
8 32 20 xxx 0
DetailStatusId for closed , pending, cancelled bla...
For example I want to select orderID : 10.
If detail lines include DetailsStatusID 1 select order Status as "Pending".
If detail lines include DetailsStatusID 2 select order Status as "Open".
If all DetailsStatusID is equal and 0 select order status as "Closed"
I tried "if exist" but couldn't success.
How can I do it ?
You can use a query like the following:
SELECT o.orderID,
CASE
WHEN MAX(CASE WHEN DetailStatusID = 1 THEN 1 ELSE 0 END) +
MAX(CASE WHEN DetailStatusID = 2 THEN 1 ELSE 0 END) +
MAX(CASE WHEN DetailStatusID = 0 THEN 1 ELSE 0 END) >= 2
THEN 'MultiStatus'
WHEN COUNT(CASE WHEN DetailStatusID = 1 THEN 1 END) > 0 THEN 'Pending'
WHEN COUNT(CASE WHEN DetailStatusID = 2 THEN 1 END) > 0 THEN 'Open'
WHEN MAX(DetailStatusID) = 0 THEN 'Closed'
END AS Status
FROM Order AS o
JOIN OrderDetails AS od ON o.orderID = od.OrderIdOfDetail
GROUP BY orderID
The query uses a CASE expression in order to calculate the Status field:
It first checks whether the order contains at least one record with DetailStatusID = 1. If it does 'Pending' is returned.
It then checks whether the order contains at least one record with DetailStatusID = 2. If it does 'Open' is returned.
It then checks whether all records of the group have DetailStatusID = 0. In this case 'Closed' is returned. I assume that DetailStatusID cannot take negative vaues.
This uses a left join to an derived table (subquery) that uses conditional aggregation to count the different DetailStatusId. This will also return a result of Other for Status if, for some reason, the OrderId has no corresponding details record.
select o.*
, [Status]=case
when od.Status0 > 0 and od.Status0 = od.DetailCount
then 'Closed'
when od.Status1 > 0
then 'Pending'
when od.Status2 > 0
then 'Open'
else 'Other'
end
from orders as o
left join (
select
OrderIdOfDetail
, DetailCount = count(*)
, Status0 = sum(case when DetailStatusId = 0 then 1 else 0 end)
, Status1 = sum(case when DetailStatusId = 1 then 1 else 0 end)
, Status2 = sum(case when DetailStatusId = 2 then 1 else 0 end)
from OrderDetails
group by OrderIdOfDetail
) as od
on o.OrderId = od.OrderIdOfDetail
rextester demo: http://rextester.com/YPA79670
query results:
+---------+---------------------+-------------+---------+
| orderID | orderDate | OrderUserId | Status |
+---------+---------------------+-------------+---------+
| 10 | 01.01.2010 00:00:00 | 1 | Pending |
| 20 | 05.05.2011 00:00:00 | 2 | Pending |
+---------+---------------------+-------------+---------+
subquery only results:
+-----------------+-------------+---------+---------+---------+
| OrderIdOfDetail | DetailCount | Status0 | Status1 | Status2 |
+-----------------+-------------+---------+---------+---------+
| 10 | 5 | 1 | 2 | 2 |
| 20 | 3 | 1 | 1 | 1 |
+-----------------+-------------+---------+---------+---------+
select *, case when DetailStatusID = '1' then 'Pending'
when DetailStatusID = '2' then 'Open'
when DetailStatusID = '0' then 'Closed'
end as OrderStatusDescription
From OrderDetails
Where OrderIdOfDetail = '10'

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 group sums by weekday in MySQL?

I have a table like this:
id | timestamp | type
-----------------------
1 2010-11-20 A
2 2010-11-20 A
3 2010-11-20 B
4 2010-11-21 A
5 2010-11-21 C
6 2010-11-27 B
and I need to count the rows for each type, grouped by weekday; like this:
weekday | A | B | C
--------------------------
5 2 2 0 -- the B column equals 2 because nov 20 and nov 27 are saturday
6 1 0 1
What would be the simplest solution for this?
I don't mind using views, variables, subqueries, etc.
Use:
SELECT WEEKDAY(t.timestamp) AS weekday,
SUM(CASE WHEN t.type = 'A' THEN 1 ELSE 0 END) AS a,
SUM(CASE WHEN t.type = 'B' THEN 1 ELSE 0 END) AS b,
SUM(CASE WHEN t.type = 'C' THEN 1 ELSE 0 END) AS c
FROM YOUR_TABLE t
GROUP BY WEEKDAY(t.timestamp)