SQL Pivot 2 Columns - sql

I have the table of the following format
I think my problem is a bit unique than the possible duplicate question, and I'm trying to get repetitive 201601...201652 columns for the two metrics orders and cost.

This is an approach for any database (including SQL Server) that does not rely on a proprietary PIVOT() function. It's a bit weird to do that for 52 weeks in such an example, though (and, to tell you the truth, the 105 resulting columns are not really the best output for the benefit of a human being reading the report).
Having said that, in this example, I do it for quarters of a year rather than weeks, and you'd just have to repeat the expressions 52 times instead of 4 times.
You could use perl or Visual Basic or whatever you prefer to generate the statement, actually.
Here goes:
-- the input table, don't use in real query ...
WITH
input(id,quarter,orders,cost) AS (
SELECT 1,201601,200,1000
UNION ALL SELECT 1,201602,300,1500
UNION ALL SELECT 1,201603,330,1800
UNION ALL SELECT 1,201604,500,2500
)
-- end of input -
SELECT
id
, SUM(CASE quarter WHEN 201601 THEN orders END) AS "orders_201601"
, SUM(CASE quarter WHEN 201602 THEN orders END) AS "orders_201602"
, SUM(CASE quarter WHEN 201603 THEN orders END) AS "orders_201603"
, SUM(CASE quarter WHEN 201604 THEN orders END) AS "orders_201604"
, SUM(CASE quarter WHEN 201601 THEN cost END) AS "cost_201601"
, SUM(CASE quarter WHEN 201602 THEN cost END) AS "cost_201602"
, SUM(CASE quarter WHEN 201603 THEN cost END) AS "cost_201603"
, SUM(CASE quarter WHEN 201604 THEN cost END) AS "cost_201604"
FROM input
GROUP BY id;
id|orders_201601|orders_201602|orders_201603|orders_201604|cost_201601|cost_201602|cost_201603|cost_201604
1| 200| 300| 330| 500| 1,000| 1,500| 1,800| 2,500

Related

Getting ORA-01476: divisor is equal to zero error

I need to get the total volume for two quarters as the query is showing in CASE statements. The third CASE statement is meant to divide the total of the volume for each quarter by the total MK_VOL.
Now the issue is that the MK_VOL for each day is one value, so essentially I need to get the MAX of each day and then SUM for the whole quarter to get the MKT_SHARE (which is SUM of VOL divided by SUM of the MK_VOL). To reiterate, for MK_VOL, it's not just a case of getting the total for the whole quarter but the MAX of each day and then the total.
As you can see the MK_VOL is the same for 02-APR.
Now my third case statement is trying to do that and I get an error. This is what I need help with please or a better solution.
I have tried doing a sub query but that didn't work either.
Data Set below
DATE Client VOL MK_VOL
----------- -------- ----- -------
01-APR AB 2 45
02-APR AC 3 46
02-APR AG1 26 46
02-APR AG2 48 46
03-APR AD 4 47
06-SEP AF 5 48
07-SEP AF 8 50
Query Below
SELECT CLIENT
, SUM(CASE WHEN DATE BETWEEN '01-JUL-19' AND '30-SEP-19' THEN VOLUME END) AS Q3_VOLUME
, SUM(CASE WHEN DATE BETWEEN '01-APR-19' AND '30-JUN-19' THEN VOLUME END) AS Q2_VOLUME
, SUM(CASE WHEN DATE BETWEEN '01-JUL-19' AND '30-SEP-19' THEN VOLUME END) AS Q3_VOLUME
/ sum(DISTINCT CASE WHEN DATE BETWEEN '01-JUL-19' AND '30-SEP-19' THEN MKT_VOL END) AS MKT_SHARE
FROM TB1
WHERE TRADE_DATE BETWEEN '01-APR-19' AND '30-SEP-19'
GROUP BY CLIENT
The expected result should be total VOL / SUM (MAX(MK_VOL) for each client.
First, your problem here is not only you could not calculate values as you wanted them to be, you had a "ORA-01476: divisor is equal to zero error" error which means you had a zero value in your divisor. You have to fix it or at least you have to avoid it like i demonstrate below.
SELECT CLIENT
SUM(Q3_VOLUME)/SUM(CASE WHEN MKT_SHARE!=0 THEN MKT_SHARE END) V1,
SUM(Q2_VOLUME)/SUM(CASE WHEN MKT_SHARE!=0 THEN MKT_SHARE END) V2
FROM (
SELECT CLIENT,
DATE
SUM(CASE WHEN DATE BETWEEN '01-JUL-19' AND '30-SEP-19' THEN VOLUME END) AS Q3_VOLUME,
SUM(CASE WHEN DATE BETWEEN '01-APR-19' AND '30-JUN-19' THEN VOLUME END) AS Q2_VOLUME,
MAX(DISTINCT CASE WHEN DATE BETWEEN '01-JUL-19' AND '30-SEP-19' THEN MKT_VOL END) AS MKT_SHARE
FROM TB1
WHERE TRADE_DATE BETWEEN '01-APR-19' AND '30-SEP-19'
GROUP BY CLIENT,DATE
)
GROUP BY CLIENT
I guess you need max of mktvoulumes for each quarter to evaluate the ratio quarter wise. Is this what you want as below?
Select
CLIENT
, SUM(CASE WHEN DATE BETWEEN
'01-JUL-19' AND '30-SEP-19' THEN
VOLUME END) )
/(
MAX(CASE WHEN DATE BETWEEN
'01-JUL-19' AND '30-SEP-19' THEN
MKT_VOLUME END) ) AS Q1
, SUM(CASE WHEN DATE BETWEEN
'01-APR-19' AND '30-JUN-19' THEN
VOLUME END)
/
(
MAX(DISTINCT CASE WHEN DATE
BETWEEN
'01-JUL-19' AND '30-SEP-19' THEN
MKT_VOLUME END) ) AS Q2
From Table group by client

How to write this SQL without duplicating the customer?

I have a table that has customers listed every month with and active_indicator. For each customer, I want to pull the active indicator for just two months (Dec 2014 and Dec 2015), but when I write the below code, I get a table where each customer is listed twice. I know I can do another step to roll up the table to the customer level using max, but is there anyway to do this in one simple SQL query?
select distinct
customer
,case when date='2015-12-01' then active_indicator else 0 end as Dec2015_active_ind
,case when date='2014-12-01' then active_indicator else 0 end as Dec2014_active_ind
from monthly_account_cust
where date in ('2015-12-01', '2014-12-01')
order by customer
Pretty sure you are looking for something like this.
select
customer
, max(case when date = '2015-12-01' then active_indicator else 0 end) as Dec2015_active_ind
, max(case when date = '2014-12-01' then active_indicator else 0 end) as Dec2014_active_ind
from monthly_account_cust
where date in ('2015-12-01','2014-12-01')
group by customer
order by customer

Case statement COUNT for THEN in (PostgreSQL) 9.3.11

Firstly, this is a coursework question, so I am not looking for a full answer, just a hint :)
I have a "monarch" database that stores name, house(?), accession, coronation(?) that keeps track of monarchs (including prime ministers). The house and coronation applies only to monarchs and returns null if monarch is a prime minister.
It looks as follows:
I am required to write a psql query that returns the scheme(house,seventeenth,eighteenth,nineteenth,twentieth), listing the number of monarchs of each royal house that acceded to the throne in the 17th,18th,19th and 20th centuries and have an issue of what to add as my THEN query.
EDIT:
Thank you for your suggestions! I made some changes to my query now:
SELECT house,
TO_CHAR(accession, 'YYYY' ) AS accession_year,
COUNT(CASE WHEN accession_year BETWEEN 1601 AND 1700 THEN name END) AS seventeenth,
COUNT(CASE WHEN accession_year BETWEEN 1701 AND 1800 THEN name END) AS eighteenth,
COUNT(CASE WHEN accession_year BETWEEN 1801 AND 1900 THEN name END) AS nineteenth,
COUNT(CASE WHEN accession_year BETWEEN 1901 AND 2000 THEN name END) AS twentieth,
FROM monarch
WHERE house IS NOT NULL
GROUP BY house
;
Now psql tells me that accession_year does not exist. I do not want to use full accession date in CASE statements. How can I still use my TO_CHAR in the query?
Each expression in a single SELECT clause is evaluated "as if" it's being computed in parallel with all other expressions in the same clause. As such, you're not allowed to have any dependencies between them since no resulting values are available at the start.
One option is to introduce a subquery:
SELECT house,
accession_year,
COUNT(CASE WHEN accession_year BETWEEN 1601 AND 1700 THEN name END) AS seventeenth,
COUNT(CASE WHEN accession_year BETWEEN 1701 AND 1800 THEN name END) AS eighteenth,
COUNT(CASE WHEN accession_year BETWEEN 1801 AND 1900 THEN name END) AS nineteenth,
COUNT(CASE WHEN accession_year BETWEEN 1901 AND 2000 THEN name END) AS twentieth,
FROM (
SELECT house,name,TO_CHAR(accession, 'YYYY' ) AS accession_year
FROM monarch
WHERE house IS NOT NULL ) AS t
GROUP BY house
;
Where now you have two separate SELECT clauses and the outer one is allowed to depend on values computed by the inner one.
You can use sub-selects OR CTEs to SELECT (or just use) calculated columns, but in some simpler cases (like yours) a LATERAL join is more readable:
SELECT house,
COUNT(CASE WHEN accession_century = 17 THEN 1 END) AS seventeenth,
COUNT(CASE WHEN accession_century = 18 THEN 1 END) AS eighteenth,
COUNT(CASE WHEN accession_century = 19 THEN 1 END) AS nineteenth,
COUNT(CASE WHEN accession_century = 20 THEN 1 END) AS twentieth
FROM monarch,
date_part('century', accession) accession_century
WHERE house IS NOT NULL
GROUP BY house
Note: to_char() returns a string, which is not really useful for querying, use date_part() or EXTRACT() instead. Especially in your case: they have the ability to extract the century, which you want to search for.

Calculating the sum of annual amounts when frequencies of payments differ

I'm currently trying to write a select statement which calculates the Annual sum in one single column.
The problem I am facing is that there are several different payment frequencies e.g. I would have to multiply a monthly amount by 12 to get the annual, quarterly by 4, semi annual by 2 etc.
I have written a statement below which does this, however it demands that I group by the frequency and amount fields, which gives the undesired result.
select (case when Frequency='month' then SUM(cast(Amount as decimal(10,2))*12)
else (case when Frequency='quarter' then SUM(cast(Amount as decimal(10,2))*4)
else (case when Frequency='year' then SUM(cast(Amount as decimal(10,2))*1)
else (case when Frequency='six months' then SUM(cast(Amount as decimal(10,2))*2) end)
end)
end) end) as 'Total Annual Amount'
from Table group by Frequency
I understand I maybe barking up the wrong tree as far as solving this problem, but the above is the closest I have gotten.
Hopefully I have been descriptive enough, if you need me to elaborate further please let me know
Try moving your sum outside of your case:
select
sum (case when Frequency='Month' then (cast(amount as decimal(10,2))*12
when Frequency='quarter' then...
end) as [Total Annual Amount]
You can also use WITH AS (assuming ORACLE)...
WITH dmap AS
( select 'Month' as duration, 12 as mult from dual
UNION
select 'Year' as duration, 1 as mult from dual
UNION
select 'Quater' as duration, 4 as mult from dual
UNION
select 'six months' as duration, 2 as mult from dual
)
select sum(cast(Amount as decimal(10,2)) * mult)
from tab, dmap
where duration = frequency;

Grabbing/rearranging data from SQL for table

I have data in sql that looks like so:
Month PersonID Level
01 102 2
01 506 1
02 617 3
02 506 1
03 297 2
And I need to query this data to receive it for use in a table that would look like this
Jan Feb March ...etc
Level 1
Level 2
Level 3
with the values being how many people are in each level each month.
I'm a complete noob with SQL so any help and relevant links to explain answers would be much appreciated.
Try this:
SELECT 'Level' + CAST(level as varchar), [January], [February], [March]
FROM (SELECT DATENAME(month, '2013'+Month+'01') Month, PersonID, Level FROM Tbl) T
PIVOT
(
COUNT(PersonID) FOR Month IN ([January], [February], [March])
) A
SQL FIDDLE DEMO
SELECT 'Level ' + CAST("Level" AS VARCHAR(2)),
SUM(CASE Month WHEN '01' THEN 1 ELSE 0 END) AS Jan,
SUM(CASE Month WHEN '02' THEN 1 ELSE 0 END) AS Feb,
SUM(CASE Month WHEN '03' THEN 1 ELSE 0 END) AS Mar,
...
FROM myTable
GROUP BY "Level"
SQL Fiddle Example
This is basically a poor man's pivot table, which should work on most RDBMS. What it does is use a SUM with a CASE to achieve a count-if for each month. That is, for January, the value for each row will be 1 if Month = '01', or 0 otherwise. Summing these values gets the total count of all "January" rows in your table.
The GROUP BY Level clause tells the engine to produce one result row for each distinct value in Level, thus separating your data by the different levels.
Since you are using SQL Server 2005, which supports PIVOT, you can simply do:
SELECT 'Level ' + CAST("Level" AS VARCHAR(2)),
[01] AS [Jan], [02] AS [Feb], [03] AS [Mar], ...
FROM myTable
PIVOT
(
COUNT(PersonId)
FOR Month IN ([01], [02], [03], ...)
) x
SQL Fiddle Example