Order by but one value is always last - sql

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

Related

How to COUNT different values without adding to GROUP BY

I have a data set that contains a name for every "job" record, and whether the job passed or failed. I want to show the Name, number of jobs, how many passed, and how many failed in one row.
I am grouping the name and using COUNT on the name to count the total number of jobs, which works fine, but I can't show how many passed and how many failed without adding them to the GROUP BY clause causing the data to separate again.
SELECT I.Name, Count(I.Name) As NumberOfJobs,
CASE WHEN WI.resultTypeID = 1 THEN COUNT(WI.resultTypeID) END AS [Passed],
CASE WHEN WI.resultTypeID = 2 THEN COUNT(WI.resultTypeID) END AS [Failed],
FROM DB.DBO.People AS I
INNER JOIN DB2.dbo.Jobs AS WI ON I.JOBID = WI.JOBID
GROUP BY I.Name, wi.resultTypeID
+-----------+-----------+--------+--------+
| Name | NumofJobs | Passed | Failed |
+-----------+-----------+--------+--------+
| Dale Test | 2 | 2 | NULL |
| Dale Test | 2 | NULL | 2 |
+-----------+-----------+--------+--------+
This is what happens when I add ResultTypeID to the GROUP BY, but I want this:
+-----------+-----------+--------+--------+
| Name | NumofJobs | Passed | Failed |
+-----------+-----------+--------+--------+
| Dale Test | 4 | 2 | 2 |
+-----------+-----------+--------+--------+
Is there anyway to do this?
You want conditional aggregation. The case expression is an argument to the aggregation function:
SELECT I.Name, Count(*) As NumberOfJobs,
SUM(CASE WHEN WI.resultTypeID = 1 THEN 1 ELSE 0 END) AS [Passed],
SUM(CASE WHEN WI.resultTypeID = 2 THEN 1 ELSE 0 END) AS [Failed],
FROM DB.DBO.People I INNER JOIN
DB2.dbo.Jobs WI
ON I.JOBID = WI.JOBID
GROUP BY I.Name;
I am guessing that wi.resultTypeID is not NULL, so I replaced the COUNT() with SUM() because I prefer SUM() in this case.
You don't need to group your query by wi.resultTypeID .
simply remove wi.resultTypeID from group by statement and put it inside aggregate function:
SELECT I.Name, Count(I.Name) As NumberOfJobs,
SUM(CASE WHEN WI.resultTypeID = 1 THEN 1 ELSE 0 END) AS [Passed],
SUM(CASE WHEN WI.resultTypeID = 2 THEN 1 ELSE 0 END) AS [Failed],
FROM DB.DBO.People AS I
INNER JOIN DB2.dbo.Jobs AS WI ON I.JOBID = WI.JOBID
GROUP BY I.Name

How to use nested case and group together the result in SQL

How to use nested case and grouped together the result.
Here is my Query:
SELECT COUNT(inc.inc_id) AS event_count,
CASE inc_data.event_type
WHEN 'c' then case inc_data.sub_event_type
when 's' then 'SR' else 'Project'
end
WHEN 'i' then 'incident'
WHEN 'p' then 'Problem'
WHEN 'd' then 'Decision'
WHEN 't' then 'Task'
end "event_sub_type"
FROM inc INNER JOIN inc_data ON inc.inc_id = inc_data.inc_id
GROUP BY inc_data.event_type, inc_data.sub_event_type
Returns:
+-------------+----------------+
| event_count | event_sub_type |
+-------------+----------------+
| 5 | Project |
| 10 | Decision |
| 15 | Incident |
| 20 | Problem |
| 25 | Task |
| 30 | SR |
+-------------+----------------+
Expected output:
+-------------+----------------+
| event_count | event_sub_type |
+-------------+----------------+
| 5 | Project |
| 25 | Others |
+-------------+----------------+
How can I modify the above query to get the expected output?
Could you try this?
SELECT COUNT(inc.inc_id) AS event_count,
(CASE WHEN (inc_data.event_type = 'c' AND inc_data.sub_event_type <> 's') THEN 'Project' ELSE 'Others' END ) "event_sub_type"
FROM inc INNER JOIN
inc_data ON inc.inc_id = inc_data.inc_id
GROUP BY (CASE WHEN (inc_data.event_type = 'c' AND inc_data.sub_event_type <> 's') THEN 'Project' ELSE 'Others' END )
How about
SELECT COUNT(inc.inc_id) AS event_count,
CASE inc_data.event_type
WHEN 'c' then case inc_data.sub_event_type
when 's' then 'Other' else 'Project'
end
ELSE 'Project'
END "event_sub_type"
FROM inc INNER JOIN inc_data ON inc.inc_id = inc_data.inc_id
GROUP BY inc_data.event_type, inc_data.sub_event_type
Based on your output, I'm assuming that you are using MySQL.
MySQL allows you to group by column numbers, so you can replace your GROUP BY clause with this:
GROUP BY 1, 2

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;

Counting sum of items of type

What i what to do is from this :
|type|quantity|
+----+--------+
|shoe| 10 |
|hat | 2 |
|shoe| 7 |
|shoe| 1 |
|hat | 5 |
to get this :
|shoes|hats|
+-----+----+
| 18 | 7 |
How can i do that? So far I hadn't come up with a working query, I think it should look something like that:
SELECT
SUM(CASE type WHEN 'shoe' then quantity ELSE 0 END) AS "shoes",
SUM(CASE type WHEN 'hat' then quantity ELSE 0 END) AS "hats"
FROM items
GROUP BY type
Just drop the group by. You want only one row:
SELECT
SUM(CASE type WHEN 'shoe' then quantity ELSE 0 END) AS "shoes",
SUM(CASE type WHEN 'hat' then quantity ELSE 0 END) AS "hats"
FROM items ;

Oracle 8i Query Help

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.)