I'm using Oracle SQL-developer and I got the following output-table, which shows the monthly sales value of our customers. The customers have several locations.
month
year
customer_name
sales_volume
01
2022
Farming company Berlin
150
01
2022
Farming company London
200
01
2022
Farming company Amsterdam
350
01
2022
XY Company Berlin
200
01
2022
customer 5
7
01
2022
customer 7
7
01
2022
X_Person
2
02
2022
XY Company London
100
02
2022
Hello Company Berlin
150
02
2022
Hello Company Amsterdam
150
02
2022
customer 1
20
02
2022
customer 2
10
02
2022
customer 3
5
02
2022
Y-Person
1
Now I'd like to get the sales_volume per customer_name for month/year. I want to add the sales_volume per month/year for all the different locations of the Farming company, the XY Company and the Hello Company. The rest (customer 1-7, X-Person, Y-Person) should be summed up in an own row named "Other"
The new output table would be the following:
month
year
customer_name
sum_Sales_volume
01
2022
Farming Company
700
01
2022
XY Company
300
01
2022
Other
16
02
2022
XY Company
100
02
2022
Hello Company
300
02
2022
Other
36
So far I tried to sum the customer_name with LIKE function but I don't understand how the "when then" works in this case.
My code:
Select
month,
year,
sum(sales_volume)
CASE
WHEN customer_name LIKE '%Farming%' Then 'Farming Company'
WHEN customer_name LIKE '%XY%' Then 'XY Company'
WHEN customer_name LIKE '%Hello%' Then 'Hello Company'
ELSE THEN 'Standard'
END AS "sum_Sales_volume"
I don't understand how the "when then" works in this case.
In a LIKE comparison, % is a wildcard character that matches zero-or-more of any character.
So, for WHEN customer_name LIKE '%Farming%', you are testing to see if the customer_name value has:
The start-of-the-string; then
Zero-or-more of any character; then
The sub-string Farming; then
Zero-or-more of any character; and
Finally, the end-of-the-string.
If it matches then the value of the THEN clause of the CASE expression is returned; otherwise the next WHEN clause is tested and so on until if none of the WHEN clauses are matched then the value from the ELSE clause is returned.
how can I sum up the values from the sales_volume column based on this string I'm searching in customer name.
Just use your CASE expression in the GROUP BY clause:
Select month,
year,
CASE
WHEN customer_name LIKE '%Farming%'
THEN 'Farming Company'
WHEN customer_name LIKE '%XY%'
THEN 'XY Company'
WHEN customer_name LIKE '%Hello%'
THEN 'Hello Company'
ELSE 'Standard'
END AS customer_name,
sum(sales_volume) AS sum_sales_volume
FROM table_name
GROUP BY
month,
year,
CASE
WHEN customer_name LIKE '%Farming%'
THEN 'Farming Company'
WHEN customer_name LIKE '%XY%'
THEN 'XY Company'
WHEN customer_name LIKE '%Hello%'
THEN 'Hello Company'
ELSE 'Standard'
END;
As MTO already answered the question, but from the comment it seems there are some more things to explain I'll try to clarify some possible missunderstandings about LIKE() function with % wildcard and especially about CASE expression. All of the below code and results refers to your sample data.
% wildcard character stands for (replaces) none or any string/array of characters
Oracle character comparisons are case sensitive
Just a few samples:
Select A_NAME From tbl Where A_NAME Like('%Berlin%'); -- any string containing 'Berlin' -with first capital letter
-- Result: Farming company Berlin
-- XY Company Berlin
-- Hello Company Berlin
Select A_NAME From tbl Where A_NAME Like('%berlin%'); -- any string containing 'berlin' -with first small letter
-- Result: no rows selected
Select A_NAME From tbl Where A_NAME Like('%1'); --any string that ends with character '1' -including just '1'
-- Result: customer 1
Select A_NAME From tbl Where A_NAME Like('cust%3'); --any string starting with 'cust' and ending with '3'
-- Result: customer 3
Select A_NAME From tbl Where A_NAME Like('Far%on'); -- any string starting with 'Far' and ending with 'on'
-- Result: Farming company London
CASE expresssion - the most important thing to know is the fact that CASE evaluates WHEN conditions until the first one satisfied (True). When that happens CASE returns the value and ends checking possible other WHEN conditions. It is very important to have this in mind when using CASE with multiple WHEN conditions.
In the below code compare two CASE expressions selected - with coresponding values of columns DESCRIPTION_1 and DESCRIPTION_2. The ressulting differences are more than obvious and the only difference between the CASE expressions used is the order of WHEN conditions.
Select A_MONTH, A_YEAR, A_NAME, VOLUME,
CASE WHEN VOLUME >= 100 THEN '100 plus'
WHEN VOLUME >= 200 THEN '200 plus'
WHEN VOLUME >= 300 THEN '300 plus'
ELSE 'less than 100'
END "DESCRIPTION_1",
CASE WHEN VOLUME >= 300 THEN '300 plus'
WHEN VOLUME >= 200 THEN '200 plus'
WHEN VOLUME >= 100 THEN '100 plus'
ELSE 'less than 100'
END "DESCRIPTION_2"
From tbl
/* R e s u l t :
A_MONTH A_YEAR A_NAME VOLUME DESCRIPTION_1 DESCRIPTION_2
------- ---------- ------------------------- ---------- ------------- -------------
01 2022 Farming company Berlin 150 100 plus 100 plus
01 2022 Farming company London 200 100 plus 200 plus
01 2022 Farming company Amsterdam 350 100 plus 300 plus
01 2022 XY Company Berlin 200 100 plus 200 plus
01 2022 customer 5 7 less than 100 less than 100
01 2022 customer 7 7 less than 100 less than 100
01 2022 X_Person 2 less than 100 less than 100
02 2022 XY Company London 100 100 plus 100 plus
02 2022 Hello Company Berlin 150 100 plus 100 plus
02 2022 Hello Company Amsterdam 150 100 plus 100 plus
02 2022 customer 1 20 less than 100 less than 100
02 2022 customer 2 10 less than 100 less than 100
02 2022 customer 3 5 less than 100 less than 100
02 2022 Y-Person 1 less than 100 less than 100
*/
Hopefuly this could help you to better understand the % wildcard, LIKE() function and CASE expression and what to have in mind combining them.
Regards...
Related
lets say that i have table like this
ITEMNAME JANUARY FEBRUARY
Table 20 100
Chair 30 50
and i want to show it like this
ITEMNAME JANUARY FEBRUARY SUM
Table 20 100 120
Chair 30 50 80
the question is how to show that table with query?
You can do it like this
SELECT item, january, february, (january+february) AS sum
FROM table
It hasn't been tested yet, but I think it works
Good morning, I've searched in the forum one doubt that I have but the results that I've seen didn't give me a solution.
I have two tables.
CARS:
Id Model
1 Seat
2 Audi
3 Mercedes
4 Ford
BREAKDOWNS:
IdBd Description Date Price IdCar
1 Engine 01/01/2020 500 € 3
2 Battery 05/01/2020 0 € 1
3 Wheel's change 10/02/2020 110,25 € 4
4 Electronic system 15/03/2020 100 € 2
5 Brake failure 20/05/2020 0 € 4
6 Engine 25/05/2020 400 € 1
I wanna make a query that shows the number of breakdowns by month with 0€ of cost.
I have this query:
SELECT Year(breakdowns.[Date]) AS YEAR, StrConv(MonthName(Month(breakdowns.[Date])),3) AS MONTH, Count(*) AS [BREAKDOWNS]
FROM cars LEFT JOIN breakdowns ON (cars.Id = breakdowns.IdCar AND breakdowns.[Price]=0)
GROUP BY breakdowns.[Price], Year(breakdowns.[Date]), Month(breakdowns.[Date]), MonthName(Month(breakdowns.[Date]))
HAVING ((Year([breakdowns].[Date]))=[Insert a year:])
ORDER BY Year(breakdowns.[Date]), Month(breakdowns.[Date]);
And the result is (if I put year '2020'):
YEAR MONTH BREAKDOWNS
2020 January 1
2020 May 1
And I want:
YEAR MONTH BREAKDOWNS
2020 January 1
2020 February 0
2020 March 0
2020 May 1
Thanks!
The HAVING condition should be in WHERE (otherwise it changes the Outer to an Inner join). But as long as you don't use columns from cars there's no need to join it.
To get rows for months without a zero price you should switch to conditional aggregation (Access doesn't support Standard SQL CASE, but IIF?).
SELECT Year(breakdowns.[Date]) AS YEAR,
StrConv(MonthName(Month(breakdowns.[Date])),3) AS MONTH,
SUM(CASE WHEN breakdowns.[Price]=0 THEN 1 ELSE 0 END) AS [BREAKDOWNS]
FROM breakdowns
JOIN cars
ON (cars.Id = breakdowns.IdCar)
WHERE ((Year([breakdowns].[Date]))=[Insert a year:])
GROUP BY breakdowns.[Price], Year(breakdowns.[Date]), Month(breakdowns.[Date]), MonthName(Month(breakdowns.[Date]))
ORDER BY Year(breakdowns.[Date]), Month(breakdowns.[Date]
I have two tables client and grouping. They look like this:
Client
C_id
C_grouping_id
Month
Profit
Grouping
Grouping_id
Month
Profit
The client table contains monthly profit for every client and every client belongs to a specific grouping scheme specified by C_grouping_id.
The grouping table contains all the groups and their monthly profits.
I'm struggling with a query that essentially calculates the monthly residual for every subscriber:
Residual= (Subscriber Monthly Profit - Grouping monthly Profit)*(average subscriber monthly profits for all months / average profits for all months for the grouping subscriber belongs to)
I have come up with the following query so far but the results seem to be incorrect:
SELECT client.C_id, client.C_grouping_Id, client.Month,
((client.Profit - grouping.profit) * (avg(client.Profit)/avg(grouping.profit))) as "residual"
FROM client
INNER JOIN grouping
ON "C_grouping_id"="Grouping_id"
group by client.C_id, client.C_grouping_Id,client.Month, grouping.profit
I would appreciate it if someone can shed some light on what I'm doing wrong and how to correct it.
EDIT: Adding sample data and desired results
Client
C_id C_grouping_id Month Profit
001 aaa jul 10$
001 aaa aug 12$
001 aaa sep 8$
016 abc jan 25$
016 abc feb 21$
Grouping
Grouping_id Month Profit
aaa Jul 30$
aaa aug 50$
aaa Sep 15$
abc Jan 21$
abc Feb 27$
Query Result:
C_ID C_grouping_id Month Residual
001 aaa Jul (10-30)*(10/31.3)=-6.38
... and so on for every month for avery client.
This can be done in a pretty straight forward way.
The main difficulty is obviously that you try to deal with different levels of aggregation at once (average of the group and the client as well as the current record).
This is rather difficult/clumsy with simple SELECT FROM GROUP BY-SQL.
But with analytical functions aka Window functions this is very easy.
Start with combining the tables and calculating the base numbers:
select c.c_id as client_id,
c.c_grouping_id as grouping_id,
c.month,
c.profit as client_profit,
g.profit as group_profit,
avg (c.profit) over (partition by c.c_id) as avg_client_profit,
avg (g.profit) over (partition by g.grouping_id) as avg_group_profit
from client c inner join grouping g
on c."C_GROUPING_ID"=g."GROUPING_ID"
and c. "MONTH" = g. "MONTH";
With this you already get the average profits by client and by grouping_id.
Be aware that I changed the data type of the currency column to DECIMAL (10,3) as a VARCHAR with a $ sign in it is just hard to convert.
I also fixed the data for MONTHS as the test data contained different upper/lower case spellings which prevented the join to work.
Finally I turned all column names into upper case to, in order to make typing easier.
Anyhow, running this provides you with the following result set:
CLIENT_ID GROUPING_ID MONTH CLIENT_PROFIT GROUP_PROFIT AVG_CLIENT_PROFIT AVG_GROUP_PROFIT
16 abc JAN 25 21 23 24
16 abc FEB 21 27 23 24
1 aaa JUL 10 30 10 31.666
1 aaa AUG 12 50 10 31.666
1 aaa SEP 8 15 10 31.666
From here it's only one step further to the residual calculation.
You can either put this current SQL into a view to make it reusable for other queries or use it as a inline view.
I chose to use it as a common table expression (CTE) aka WITH clause because it's nice and easy to read:
with p as
(select c.c_id as client_id,
c.c_grouping_id as grouping_id,
c.month,
c.profit as client_profit,
g.profit as group_profit,
avg (c.profit) over (partition by c.c_id) as avg_client_profit,
avg (g.profit) over (partition by g.grouping_id) as avg_group_profit
from client c inner join grouping g
on c."C_GROUPING_ID"=g."GROUPING_ID"
and c. "MONTH" = g. "MONTH")
select client_id, grouping_id, month,
client_profit, group_profit,
avg_client_profit, avg_group_profit,
round( (client_profit - group_profit)
* (avg_client_profit/avg_group_profit), 2) as residual
from p
order by grouping_id, month, client_id;
Notice how easy to read the whole statement is and how straight forward the residual calculation is done.
The result is then this:
CLIENT_ID GROUPING_ID MONTH CLIENT_PROFIT GROUP_PROFIT AVG_CLIENT_PROFIT AVG_GROUP_PROFIT RESIDUAL
1 aaa AUG 12 50 10 31.666 -12
1 aaa JUL 10 30 10 31.666 -6.32
1 aaa SEP 8 15 10 31.666 -2.21
16 abc FEB 21 27 23 24 -5.75
16 abc JAN 25 21 23 24 3.83
Cheers,
Lars
How can I make a static column/row in crosstab? See example below; can I have a fixed jan, feb, march, ... columns instead of it generating dynamically?
location jan feb mar apr may jun jul aug sep oct nov dec
london 500 62 200 50 0 60 100 46 89 200 150 210
paris 50 26 20 500 50 70 40 200 0 40 250 50
I want the column (jan, feb, mar, apr, ...) to always show up regardless of their measures zero or have values. Like they are fixed.
Here is the query I'm using:
select sum("AMOUNT"), "REQUESTDATE","description"
from(
SELECT SUM(e.AMOUNT)"AMOUNT",TO_CHAR(REQUESTDATE,'MM')"REQUESTDATE", CA.DESCR "description"
FROM PC_PAYMENTTRXNLOG PC,GLB_TYPE ca, PC_ESERVICEINQUIRY e
where PC.ESERVICE_ID = E.ID
AND trunc(REQUESTDATE) between trunc(to_date('2012-01-01','yyyy-mm-dd')) and trunc(to_date('2012-06-30','yyyy-mm-dd'))
GROUP BY TO_CHAR(REQUESTDATE,'MM'),CA.DESCR
)
group by "REQUESTDATE","description"
and the output
SUM("amount") Requestdate Description
2550405 04 A
2550405 04 B
23893281 05 C
614977 06 A
614977 06 E
2550405 04 C
now after updated the query to be
select sum("AMOUNT"), month,"description"
from(
SELECT SUM(e.AMOUNT)"AMOUNT",TO_CHAR(REQUESTDATE,'MM')"REQUESTDATE", CA.DESCR "description"
FROM PC_PAYMENTTRXNLOG PC,GLB_TYPE ca, PC_ESERVICEINQUIRY e
where PC.ESERVICE_ID = E.ID
AND trunc(REQUESTDATE) between trunc(to_date('2012-01-01','yyyy-mm-dd')) and trunc(to_date('2012-06-30','yyyy-mm-dd'))
GROUP BY TO_CHAR(REQUESTDATE,'MM'),CA.DESCR
)
full outer join (select to_char(date '1970-01-01'
+ numtoyminterval(level - 1, 'month'), 'mm') as month
from dual
connect by level <= 12) on month="REQUESTDATE"
group by month,"description"
when run the query run it displaying all the months regardless of their measures zero or have values.
BUT now the output is like that
location jan feb mar apr may jun jul aug sep oct nov dec
london 500 62 200 50 0 60 100 46 89 200 150 210
paris 50 26 20 500 50 70 40 200 0 40 250 50
null 0 0 0 0 0 0 0 0 0 0 0 0
how i can restrict/hide the last null row?
have not tested it.. but try something like this
select sum("AMOUNT"), month,"description"
from(SELECT SUM(e.AMOUNT)"AMOUNT",TO_CHAR(REQUESTDATE,'MM')"REQUESTDATE", CA.DESCR "description"
FROM PC_PAYMENTTRXNLOG PC,GLB_TYPE ca, PC_ESERVICEINQUIRY e
where PC.ESERVICE_ID = E.ID
AND trunc(REQUESTDATE) between trunc(to_date('2012-01-01','yyyy-mm-dd')) and trunc(to_date('2012-06-30','yyyy-mm-dd'))
GROUP BY TO_CHAR(REQUESTDATE,'MM'),CA.DESCR
)
full outer join (select to_char(date '1970-01-01'
+ numtoyminterval(level - 1, 'month'), 'mm') as month
from dual
connect by level <= 12) on month="REQUESTDATE"
group by month,"description"
click here for SQL Fiddle demo to generate 1 to 12 in Oracle
Once you have generated this.. full outer join Your main query with this series query and take month from series query as I did in main query.
Using this query you will get all the data for all months with null values in measure.
For Description column - iReport set property's isRemoveLineWhenBlank and isBlankWhenNull to True, This will remove the null value being printed in iReport
For Measure use Print when expression in such a way, when ever description is null then return false. So this will prevent the value 0 being printed in iReport.
I want to list BR, BRANCHNAME and the number of people employed in it. There are 5 branches it total and only 4 of them have people employed in it; Branch 05 has no employees in it. After using the following code, the branch 05 will not be shown as the row of branch 05 will not be included after the where statement. I want to show a row of "05 Br05 0".
SELECT EMPLOYEE.BR, BRANCHNAME, Count(*) AS Number
FROM EMPLOYEE, BRANCH
WHERE (EMPLOYEE.BR = BRANCH.BR)
GROUP BY EMPLOYEE.BR, BRANCHNAME;
The result is:
BR BRANCHNAME Number
01 Br01 6
02 Br02 4
03 Br03 5
04 Br04 6
I want to have the following result:
BR BRANCHNAME Number
01 Br01 6
02 Br02 4
03 Br03 5
04 Br04 6
05 Br05 0
It would seem you want a LEFT JOIN which gives a countable row with a null result even if there is no matching employee.
Since you've not added your table structure, I assume branchname is a field in the branch table.
SELECT branch.br, branch.branchname, COUNT(employee.br) AS Number
FROM branch
LEFT JOIN employee
ON branch.br = employee.br
GROUP BY branch.br, branch.branchname
An SQLfiddle to test with (based on SQL Server since Access is not available)