Oracle 8i Query Help - sql

I have a table that looks something like this:
ID | STATUS | TYPE
----------------------------------------
x123 | A | High School
x122 | I | High School
x124 | F | High School
x125 | A | College
x126 | I | College
x127 | F | College
x128 | F | College
Can anyone help me come up with a query for oracle 8i that displays this table like this
Type | Count A | Count I | Count F
------------------------------------------------------------
High School | 1 | 1 | 1
College | 1 | 1 | 2
Thanks!

Here's one approach:
select t.type as "Type"
, sum(case when t.status = 'A' then 1 else 0 end) as "Count A"
, sum(case when t.status = 'I' then 1 else 0 end) as "Count I"
, sum(case when t.status = 'F' then 1 else 0 end) as "Count F"
from my_table t
group by t.type
order by t.type desc
This works if you have specific columns you want returned, and works for "counting" rows that meet more complex criteria set.
[EDIT]
(Added the DESC keyword to get the result set ordered as shown by OP, +1 good catch by Rob van Wijk!)
(Andomar makes a good observation, with more and more columns in the result set, using this approach, the statement gets unweildy. There are other approaches to getting the same result set which work well if the only "test" is an equality comparison on a single column.)
Oracle 8i does support the CASE expression, doesn't it? Oracle 8 didn't, if I recall correctly. We can go "old school" to do the same thing with the DECODE function:
select t.type as "Type"
, sum(decode(t.status,'A',1,0)) as "Count A"
, sum(decode(t.status,'I',1,0)) as "Count I"
, sum(decode(t.status,'F',1,0)) as "Count F"
from my_table t
group by t.type
order by t.type DESC
[/EDIT]
Sometimes, we want to check more than one type condition, and include a row in more than one count. We can get a total
select t.type as "Type"
, sum(case when t.status in ('A') then 1 else 0 end) as "Count A"
, sum(case when t.status in ('I') then 1 else 0 end) as "Count I"
, sum(case when t.status in ('F') then 1 else 0 end) as "Count F"
, sum(case when t.status in ('A','I') then 1 else 0 end) as "#AI"
, sum(decode(sign(t.foo-t.bar),1,1,0)) as "#foo>bar"
, sum(decode(sign(10.0-t.foo),1,1,0)) as "#foo<10"
from my_table t
group by t.type
order by t.type desc
(Just to point out, it's possible for a row to satisfy the specified criteria for multiple columns, and so it could be "counted" more than once. Sometimes, that's exactly what we want.)

Related

Calculate difference in Oracle table from sum

I have a table which looks as followed:
ID | Value
A | 2
A | 5
A | 6
B | 1
B | 7
B | -3
I am currently using a statement as followed
select ID, sum(VALUE)
where ...
group by ID.
Now I need the difference from A and B.
Could anyone send me on the right path? I am working with Oracle.
Use conditional aggregation:
SELECT SUM(CASE WHEN id = 'A' THEN "Value" ELSE 0 END) -
SUM(CASE WHEN id = 'B' THEN "Value" ELSE 0 END) "Difference"
FROM tablename;
See the demo.

Order by but one value is always last

I was wandering if it was possible to Order By a column but have a value always come last.
For instance, I'm doing a COUNT to see how many sales our employees have, differentiated by type of employee.
This is what I'm getting:
+-----------+------------+------------+
| Status | Associate | Internal |
+-----------+------------+------------+
Agended | 5 | 13
Canceled | 0 | 1
Instaled | 10 | 24
TOTAL | 15 | 38
And this is my goal:
+-----------+------------+------------+
| Status | Associate | Internal |
+-----------+------------+------------+
Canceled | 0 | 1
Agended | 5 | 13
Instaled | 10 | 24
TOTAL | 15 | 38
My code is
SELECT CASE WHEN bo.stateofcontract IS NULL THEN ISNULL(bo.stateofcontract,'TOTAL') ELSE bo.stateofcontract END "Status"
, COUNT(CASE WHEN bo.stateofcontract IN ('INSTALED','AGENDED','CANCELED')AND CM3.func = 'ASSOCIATE' THEN 1 END) "Associate"
, COUNT(CASE WHEN bo.stateofcontract IN ('INSTALED','AGENDED','CANCELED')AND CM3.func <> 'ASSOCIATE' THEN 1 END) "Internal"
FROM BO
JOIN CM3 ON CM3.cm = BO.employee
WHERE (bo.stateofcontract IN ('INSTALED','AGENDED','CANCELED'))
GROUP BY ROLLUP (bo.stateofcontract)
You can simplify your query a bit by removing some conditions from the CASE expressions. There is also a little trick in SQL Server using CHARINDEX() which usually works for such ordering:
SELECT COALESCE(bo.stateofcontract, 'TOTAL') as status,
COUNT(CASE WHEN CM3.func = 'ASSOCIATE' THEN 1 END) Associate,
COUNT(CASE WHEN AND CM3.func <> 'ASSOCIATE' THEN 1 END) as Internal
FROM BO JOIN
CM3
ON CM3.cm = BO.employee
WHERE bo.stateofcontract IN ('INSTALED', 'AGENDED', 'CANCELED'))
GROUP BY ROLLUP bo.stateofcontract
ORDER BY CHARINDEX(status, 'Canceled,Agended,Instaled,Total');
You can ORDER BY a CASE expression, or add an ordering column in the SELECT. EG:
SELECT CASE WHEN bo.stateofcontract IS NULL THEN ISNULL(bo.stateofcontract,'TOTAL') ELSE bo.stateofcontract END "Status"
, COUNT(CASE WHEN bo.stateofcontract IN ('INSTALED','AGENDED','CANCELED')AND CM3.func = 'ASSOCIATE' THEN 1 END) "Associate"
, COUNT(CASE WHEN bo.stateofcontract IN ('INSTALED','AGENDED','CANCELED')AND CM3.func <> 'ASSOCIATE' THEN 1 END) "Internal"
FROM BO
JOIN CM3 ON CM3.cm = BO.employee
WHERE (bo.stateofcontract IN ('INSTALED','AGENDED','CANCELED'))
GROUP BY ROLLUP (bo.stateofcontract)
ORDER BY case when Status = 'Canceled' then 1
when Status = 'Agended' then 2
when Status = 'Instaled' then 3
when Status = 'Total' then 4
end

multiple record in a single row

I have post graduation degree records of students in my database. student may have only one post graduation degree, some students may have more than one post graduation degree.
rollno | pgdegree | score
--------------------------
0001 | 41 | 56
0002 | 42 | 78
0002 | 49 | 75
0003 | 48 | 77
Here roll no. 0002 is more than one time and roll no. 0001,0003 are only one time.
i want my desired output as :
rollno | pgdegree1 | score1 | pgdegree2 | score2
------------------------------------------------
0001 | 41 | 56 | |
0002 | 42 | 78 | 49 | 75
0003 | 48 | 77 | |
Note : in my database any student can have one or two post gradation only. Not more than two PG degree.
Here is another solution using ROW_NUMBER() and conditional aggregation to save some unnecessary SELECTs :
SELECT s.rollno,
MAX(CASE WHEN s.rnk = 1 THEN s.pgdegree END) AS pgdegree1,
MAX(CASE WHEN s.rnk = 1 THEN s.score END) AS score1,
MAX(CASE WHEN s.rnk = 2 THEN s.pgdegree END) AS pgdegree2,
MAX(CASE WHEN s.rnk = 2 THEN s.score END) AS score2
FROM
(
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY t.rollno ORDER BY t.pgdegree, t.score) AS rnk
FROM YourTable t
) s
GROUP BY s.rollno
Do a self LEFT JOIN to add second pgdegree if available for a rollno. Do NOT EXISTS to only return rows with lowest pgdegree as t1.pgdegree.
select t1.rollno, t1.pgdegree, t1.score, t2.pgdegree, t2.score
from tablename t1
left join tablename t2
on t1.rollno = t2.rollno and t1.pgdegree < t2.pgdegree
where not exists (select * from tablename t3
where t1.rollno = t3.rollno
and t1.pgdegree > t3.pgdegree)
You can use a clever pivot query:
SELECT t.rollno,
SUM(CASE WHEN pgdegree = (SELECT MIN(pgdegree) FROM yourTable WHERE rollno = t.rollno)
THEN pgdegree ELSE 0 END) AS pgdegree1,
SUM(CASE WHEN pgdegree = (SELECT MIN(pgdegree) FROM yourTable WHERE rollno = t.rollno)
THEN score ELSE 0 END) AS score1,
SUM(CASE WHEN pgdegree = (SELECT MAX(pgdegree) FROM yourTable WHERE rollno = t.rollno)
THEN pgdegree ELSE 0 END) AS pgdegree2,
SUM(CASE WHEN pgdegree = (SELECT MAX(pgdegree) FROM yourTable WHERE rollno = t.rollno)
THEN score ELSE 0 END) AS score2
FROM yourTable t
GROUP BY t.rollno
Explanation:
The first two CASE statements have subqueries which will return pgdegree if that value happens to be the minimum value for that given rollno. This pgdegree and score will appear as the first two columns. Similarly, the last two CASE statements use the maximum value to generate the second two columns.
select
rollno,
(array_agg(pgdegree))[1] as pgdegree1,
(array_agg(score))[1] as score1,
(array_agg(pgdegree))[2] as pgdegree2,
(array_agg(score))[2] as score2
from
your_table
group by
rollno;

SQL calculate the sum of a column based on the date in two differents variables

I've a simple table in this forme :
BillItem (id,amount, volume, bill_date,....other fields)
I want to obtain in my query 4 differents sum of fields amount and volume based on the date
for example, in my table i've this data :
Id | amount | volume | bill_date | libelle
1 | 10 | 50 | 02/04/2016| bill1
2 | 20 | 55 | 02/04/2016| bill1
2 | 88 | 66 | 02/05/2016| bill1
3 | 30 | 60 | 03/05/2016| bill2
4 | 40 | 10 | 02/04/2016| bill3
5 | 50 | 20 | 02/05/2016| bill3
and the result must be like this :
bill1, sum_date_1=30, sum_date_2=88, sum_volume_date_1=105, sum_volume_date_2=66
bill2, sum_date_1=0, sum_date_2=30, sum_volume_date_1=0, sum_volume_date_2=60
bill3, sum_date_1=40, sum_date_2=50, sum_volume_date_1=10, sum_volume_date_2=20
i've this query with only two sum variable :
select ans.SERVICE_TYPE, ans.SERVICE_SUB_TYPE,
sum(bi.ACTUAL_AMOUNTVAT),sum(bi.ACTUAL_VOLUME), bi.BILL_DATE
from bill_item bi left outer join ANALYTIC_SECTION ans on ans.TREE_PATH=bi.REPORT_SECTION
where bi.account_id=7
and bi.BILL_DATE<='31/05/2016' and bi.BILL_DATE>='01/04/2016'
and ans.REPORT_TYPE='ARPE_REPORT' and ans.ACCOUNT_ID=7
group by ans.SERVICE_TYPE, ans.SERVICE_SUB_TYPE, bi.BILL_DATE;
Is it possible to obtain two differents sum for each field (amount and volume) ?
I've resolved the query like this :
select distinct ans.SERVICE_TYPE, ans.SERVICE_SUB_TYPE,
sum(Case when bi.BILL_DATE<'01/05/2016' then bi.ACTUAL_AMOUNTVAT ELSE 0 END) as amount_m1,
sum(Case when bi.BILL_DATE>='01/05/2016' then bi.ACTUAL_AMOUNTVAT ELSE 0 END) as amount_m,
sum(Case when bi.BILL_DATE<'01/05/2016' then bi.ACTUAL_VOLUME ELSE 0 END) as volume_m1,
sum(Case when bi.BILL_DATE>='01/05/2016' then bi.ACTUAL_VOLUME ELSE 0 END) as volume_m
--,bi.BILL_DATE
from bill_item bi left outer join ANALYTIC_SECTION ans on ans.TREE_PATH=bi.REPORT_SECTION
where bi.account_id=7
and bi.BILL_DATE<='06/05/2016' and bi.BILL_DATE>='06/04/2016'
and ans.REPORT_TYPE='ARPE_REPORT' and ans.ACCOUNT_ID=7
group by ans.SERVICE_TYPE, ans.SERVICE_SUB_TYPE;
Thank's All for your help
it looks to me like you want to summarize by each date and then return the value for the months in ascending fields?
It might work with a subquery and a ranking option, although this may not be the most efficient route.
Select t.Id
, Max(Case When t.BillRank=1 Then t.SumVolume Else Null End) As Volume1
, Max(Case When t.BillRank=2 Then t.SumVolume Else Null End) As Volume2
, Max(Case When t.BillRank=1 Then t.SumVAT Else Null End) As Vat1
, Max(Case When t.BillRank=2 Then t.SumVAT Else Null End) As Vat2
From ( Select ans.SERVICE_TYPE
, ans.SERVICE_SUB_TYPE
, Sum(bi.ACTUAL_AMOUNTVAT) As SumVAT
, Sum(bi.ACTUAL_VOLUME) As SumVolume
, bi.BILL_DATE
, bi.Id
, Rank() Over ( Partition By bi.Id Order By bi.BILL_DATE Asc ) As BillRank
From bill_item As bi
Left Outer Join ANALYTIC_SECTION as ans
On ans.TREE_PATH = bi.REPORT_SECTION
Group By ans.SERVICE_TYPE
, ans.SERVICE_SUB_TYPE
, bi.BILL_DATE
, bi.Id
) t
Group By t.Id;

using sql count in a case statement

I have a table and i need to present the output in the following fashion.
tb_a:
col1 | reg_id | rsp_ind
Count of rows with rsp_ind = 0 as 'New' and 1 as 'Accepted'
The output should be
NEW | Accepted
9 | 10
I tried using the following query.
select
case when rsp_ind = 0 then count(reg_id)end as 'New',
case when rsp_ind = 1 then count(reg_id)end as 'Accepted'
from tb_a
and i m getting output as
NEW | Accepted
NULL| 10
9 | NULL
Could someone help to me tweak the query to achieve the output.
Note : I cannot add a sum surrounding this. Its part of a bigger program and so i cannot add a super-query to this.
SELECT
COUNT(CASE WHEN rsp_ind = 0 then 1 ELSE NULL END) as "New",
COUNT(CASE WHEN rsp_ind = 1 then 1 ELSE NULL END) as "Accepted"
from tb_a
You can see the output for this request HERE
Depending on you flavor of SQL, you can also imply the else statement in your aggregate counts.
For example, here's a simple table Grades:
| Letters |
|---------|
| A |
| A |
| B |
| C |
We can test out each Aggregate counter syntax like this (Interactive Demo in SQL Fiddle):
SELECT
COUNT(CASE WHEN Letter = 'A' THEN 1 END) AS [Count - End],
COUNT(CASE WHEN Letter = 'A' THEN 1 ELSE NULL END) AS [Count - Else Null],
COUNT(CASE WHEN Letter = 'A' THEN 1 ELSE 0 END) AS [Count - Else Zero],
SUM(CASE WHEN Letter = 'A' THEN 1 END) AS [Sum - End],
SUM(CASE WHEN Letter = 'A' THEN 1 ELSE NULL END) AS [Sum - Else Null],
SUM(CASE WHEN Letter = 'A' THEN 1 ELSE 0 END) AS [Sum - Else Zero]
FROM Grades
And here are the results (unpivoted for readability):
| Description | Counts |
|-------------------|--------|
| Count - End | 2 |
| Count - Else Null | 2 |
| Count - Else Zero | 4 | *Note: Will include count of zero values
| Sum - End | 2 |
| Sum - Else Null | 2 |
| Sum - Else Zero | 2 |
Which lines up with the docs for Aggregate Functions in SQL
Docs for COUNT:
COUNT(*) - returns the number of items in a group. This includes NULL values and duplicates.
COUNT(ALL expression) - evaluates expression for each row in a group, and returns the number of nonnull values.
COUNT(DISTINCT expression) - evaluates expression for each row in a group, and returns the number of unique, nonnull values.
Docs for SUM:
ALL - Applies the aggregate function to all values. ALL is the default.
DISTINCT - Specifies that SUM return the sum of unique values.
Close... try:
select
Sum(case when rsp_ind = 0 then 1 Else 0 End) as 'New',
Sum(case when rsp_ind = 1 then 1 else 0 end) as 'Accepted'
from tb_a
The reason you're getting two rows instead of one is that you are grouping by rsp_ind in the outer query (which you did not, to my disappointment, share with us). There is nothing you can do to force one row instead of two without dealing with that GROUP BY item.
CREATE TABLE #CountMe (Col1 char(1));
INSERT INTO #CountMe VALUES ('A');
INSERT INTO #CountMe VALUES ('B');
INSERT INTO #CountMe VALUES ('A');
INSERT INTO #CountMe VALUES ('B');
SELECT
COUNT(CASE WHEN Col1 = 'A' THEN 1 END) AS CountWithoutElse,
COUNT(CASE WHEN Col1 = 'A' THEN 1 ELSE NULL END) AS CountWithElseNull,
COUNT(CASE WHEN Col1 = 'A' THEN 1 ELSE 0 END) AS CountWithElseZero
FROM #CountMe;
If you want to group the results based on a column and take the count based on the same, you can run the query as :
$sql = "SELECT COLUMNNAME,
COUNT(CASE WHEN COLUMNNAME IN ('YOURCONDITION') then 1 ELSE NULL END) as 'New',
COUNT(CASE WHEN COLUMNNAME IN ('YOURCONDITION') then 1 ELSE NULL END) as 'ACCPTED'
FROM TABLENAME
GROUP BY COLUMNANME";
select sum(rsp_ind = 0) as `New`,
sum(rsp_ind = 1) as `Accepted`
from tb_a
ok. I solved it
SELECT `smart_projects`.project_id, `smart_projects`.business_id, `smart_projects`.title,
`page_pages`.`funnel_id` as `funnel_id`, count(distinct(page_pages.page_id) )as page_count, count(distinct (CASE WHEN page_pages.funnel_id != 0 then page_pages.funnel_id ELSE NULL END ) ) as funnel_count
FROM `smart_projects`
LEFT JOIN `page_pages` ON `smart_projects`.`project_id` = `page_pages`.`project_id`
WHERE smart_projects.status != 0
AND `smart_projects`.`business_id` = 'cd9412774edb11e9'
GROUP BY `smart_projects`.`project_id`
ORDER BY `title` DESC