How to get dynamic columns for a query - mysql

I want to create a SQL query which should generate columns according to date range.
For example, if my input parameters for date range in 2013-01-01 and 2013-06-07. Then the query should dynamically create seven month columns between these above two dates. The output should look like this:
data column1 column2 Jan-13 feb-13 mar-13 apr-13 may-13 jun-13 jul-13
I have tried couple of times but not reached to the desired result.
Please share your ideas on this. How should I proceed?
SELECT a.* ,
(SELECT SUM(fid1.TOTAL_AMOUNT) FROM
fact_invoice_details fid1
WHERE DATE_FORMAT(dd.thedate, '%b %y') LIKE '%Jan%' ) AS 'Jan',
(SELECT SUM(fid1.TOTAL_AMOUNT) FROM
fact_invoice_details fid1
where DATE_FORMAT(dd.thedate, '%b %y') LIKE '%Feb%' ) AS 'Feb'
FROM
(
SELECT DISTINCT gl.ACCOUNT_NUMBER,
gl.NAME AS 'GL_Account_Name',
di.account_id 'ACCOUNT_ID',
dpp.company_name 'ACCOUNT_NAME',
dd3.thedate AS 'INVOICE_DATE',
di.OCCURRED_ON_DATE_ID,
di.INVOICE_ID ,
di.INVOICE_NUMBER ,
fid.INVOICE_ITEM_ID,
dpro.sku,
dpro.PRODUCT_NAME,
dci.ISO,
fid.TOTAL_AMOUNT
-- DATE_FORMAT(dd3.thedate, '%b %y') as mon
FROM dim_gl_account gl
/* Some tables also having here */
WHERE dte.TENANT_ID = 155
AND dd3.thedate BETWEEN CAST('2013-01-01' AS DATE) AND CAST('2013-03-01' AS DATE) ) a
as i can not paste the whole code .

What you are attempting to do is pivot the data from rows into columns. Unfortunately MySQL does not have this capability, however, we can use an aggregate function with a CASE statement to get a similar result.
SELECT
cid,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 1 THEN total ELSE 0 END) AS JAN_13,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 2 THEN total ELSE 0 END) AS FEB_13,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 3 THEN total ELSE 0 END) AS MAR_13,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 4 THEN total ELSE 0 END) AS ARR_13,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 5 THEN total ELSE 0 END) AS MAY_13,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 6 THEN total ELSE 0 END) AS JUN_13,
SUM(CASE WHEN YEAR(duedate) = 2013 AND MONTH(duedate) = 7 THEN total ELSE 0 END) AS JUL_13
FROM invoices
GROUP BY cid
DEMO
Basically we supplied a CASE line for each month/year that we want to get the SUM for. You could include a line for each month/year and add back the date ranges to your WHERE clause but this would still create columns for all the months with zero values as seen here.
A better way to do this dynamically would be to use a prepared statement. This way we can supply our date range in the WHERE clause and let SQL do the heavy lifting.
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN MonthYear = ''',
MonthYear,
''' THEN total ELSE 0 END) AS ',
MonthYear
)
) INTO #sql
FROM (
SELECT
cid,
DATE_FORMAT(duedate, '%b_%y') MonthYear,
total
FROM invoices
WHERE duedate BETWEEN CAST('2013-01-01' AS DATE) AND CAST('2013-07-31' AS DATE)
ORDER BY duedate
) src;
SET #sql = CONCAT("SELECT cid, ", #sql, "
FROM (
SELECT
cid,
DATE_FORMAT(duedate, '%b_%y') MonthYear,
total
FROM invoices
WHERE duedate BETWEEN CAST('2013-01-01' AS DATE) AND CAST('2013-07-31' AS DATE)
ORDER BY duedate
) src
GROUP BY cid");
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
DEMO
My examples are obviously much simpler then the snippet you provided but you should be able to incorporate the logic back into your queries. Depending on how you will be making calls you might also consider using a stored procedure as well. Finally, here is a link to an article discussing pivot like functions for MySQL.
Dynamic pivot tables (transform rows to columns)

Related

How to change the mysql query to retrieve data (pivot view)

I have a dataset as follows,
I need to get the average xdr_count of each table_name for 7 past days..So I tried as below.
SELECT
t1.table_date,
t1.table_name,
t1.xdr_count,
t2.AVG_XDR,
t2.Threshold
FROM stat t1
INNER JOIN
(
SELECT table_name, AVG(xdr_count) AS AVG_XDR,AVG(xdr_count)*.8 as Threshold
FROM stat
where table_date >= DATE(NOW() - INTERVAL 7 DAY)
GROUP BY table_name
) t2
ON t2.table_name = t1.table_name
where table_date >= DATE(NOW() - INTERVAL 7 DAY);
This works fine and I could get the output as below.
But I need to rearrange this table to a pivot view(hope this can called as pivot view) such as below.
I need to mention the table name,Avg(XDR) and dates with appropraite xdr_count values.
Can someone help me to change the view of output result..
Note: Threshold value will be use later.
I'm not entirely sure how you want to calculate the avg_xdr columns. But, you can try something like this.
SELECT table_name,
MAX(CASE WHEN table_date = '2022-08-21' then avg_xdr else 0 END) AS avg_xdr,
MAX(CASE WHEN table_date = '2022-08-21' then xdr_count else 0 END) AS '2022-08-21',
MAX(CASE WHEN table_date = '2022-08-22' then xdr_count else 0 END) AS '2022-08-22',
MAX(CASE WHEN table_date = '2022-08-23' then xdr_count else 0 END) AS '2022-08-23',
MAX(CASE WHEN table_date = '2022-08-24' then xdr_count else 0 END) AS '2022-08-24'
FROM tableA
Group by table_name
db fiddle

How to aggregate values inside a column in SQL?

I could not solve this question, any help?
Batsman_detail table has the columns ,
[Opposition (varchar), runs(int), dismissal(varchar), venue(varchar)]:
opposition column has may countries in it.
runs column has the amount of runs scored by the batsman in that match.
dimissal column has any of the following values caught/bowled/run out/not out.
venue column has the stadium name where the match is held.
Now you should write a query such that the output table must have that number of times the batsman had gone out in a type of dismissal against the countries Pakistan,Australia and Srilanka (Original table contains many countries.),
Example output format :
opposition caught bowled runout notout
Pakistan 3 5 7 9
Australia 0 4 4 1
Sri Lanka 4 2 3 4
You seem to want conditional aggregation:
select opposition,
sum(case when dismissal = 'caught' then runs else 0 end) caught,
sum(case when dismissal = 'bowled' then runs else 0 end) bowled,
sum(case when dismissal = 'run out' then runs else 0 end) run_out,
sum(case when dismissal = 'not out' then runs else 0 end) not_out
from batsman
group by opposition
This sums the runs for each value of dismissal. If you just want to count the occurences, then:
select opposition,
sum(dismissal = 'caught') caught,
sum(dismissal = 'bowled') bowled,
sum(dismissal = 'run out') run_out,
sum(dismissal = 'not out') not_out
from batsman
group by opposition
You can distinctly select all dismissal cases and then pivot dynamically as
SET #sql = NULL;
SELECT GROUP_CONCAT(
CONCAT(
'SUM(CASE WHEN dismissal =''', dismissal, ''' THEN runs ELSE 0 END ) AS ',
dismissal
)
)
INTO #sql
FROM ( SELECT DISTINCT dismissal
FROM Batsman_detail
) b;
SET #sql = CONCAT('SELECT opposition,',#sql,
' FROM Batsman_detail
GROUP BY opposition');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Demo

SQL: Creating new columns using Case When, Grouping_ID

How do I create an entirely new column in SQL while using CASE WHEN, GROUPING_ID(), and ROLLUP() syntax?
So far I have tried:
SELECT Country, ContactTitle, COUNT(ContactTitle) AS Count
,CASE(
WHEN
GROUPING_ID(Legend) = 0 THEN ' '
WHEN
GROUPING_ID(Legend) = 1 THEN 'SUBTOTAL(Country)')
GROUP BY ROLLUP(Country, ContactTitle)
FROM dbo.Customers
SELECT Country, ContactTitle, COUNT(ContactTitle) AS Count,
CASE
WHEN GROUPING_ID(Country,ContactTitle) = 0 THEN ''
WHEN GROUPING_ID(Country,ContactTitle) = 1 THEN CONCAT('Subtotal for ',Country)
END AS Legend
FROM dbo.Customers
GROUP BY ROLLUP(Country, ContactTitle);
A CASE needs an END.
And while testing, you could output the GROUPING_ID or GROUPING to understand what they return.
SELECT Country, ContactTitle, COUNT(*) AS Count,
(CASE
WHEN GROUPING_ID(Country, ContactTitle) = 1
THEN CONCAT('SUBTOTAL(',Country,')')
WHEN GROUPING(Country) = 1
THEN 'TOTAL'
ELSE ' '
END) AS Legend
FROM Customers
GROUP BY Country, ContactTitle WITH ROLLUP;

mysql query split column into m

I have the following database structure:
FieldID|Year|Value
a|2011|sugar
a|2012|salt
a|2013|pepper
b|2011|pepper
b|2012|pepper
b|2013|pepper
c|2011|sugar
c|2012|salt
c|2013|salt
now I would like to run a query that counts the number of fields for every item in the particular year looking something like this:
value|2011|2012|2013
sugar|2|0|0
salt |0|2|1
pepper|1|1|2
I used multiple tables for every year before. However the distinct values for 2011,2012 and 2013 might be different (e.g. sugar would only be present in 2011)
For individual years I used:
SELECT `Value`, COUNT( `FieldID` ) FROM `Table` WHERE `Year`=2011 GROUP BY `Value`
A1ex07's answer is fine. However, in MySQL, I prefer this formulation:
SELECT Value,
sum(`Year` = 2011) AS cnt2011,
sum(`Year` = 2012) AS cnt2012,
sum(`Year` = 2013) AS cnt2013
FROM t
GROUP BY value;
The use of count( . . . ) produces the correct answer, but only because the else clause is missing. The default value is NULL and that doesn't get counted. To me, this is a construct that is prone to error.
If you want the above in standard SQL, I go for:
SELECT Value,
sum(case when `Year` = 2011 then 1 else 0 end) AS cnt2011,
sum(case when `Year` = 2012 then 1 else 0 end) AS cnt2012,
sum(case when `Year` = 2013 then 1 else 0 end) AS cnt2013
FROM t
GROUP BY value;
You can do pivoting :
SELECT `Value`,
COUNT(CASE WHEN `Year` = 2011 THEN FieldID END) AS cnt2011,
COUNT(CASE WHEN `Year` = 2012 THEN FieldID END) AS cnt2012,
COUNT(CASE WHEN `Year` = 2013 THEN FieldID END) AS cnt2013
FROM `Table`
GROUP BY `Value`
It is called Pivot Table, achieve with a chain of CASE statements which apply a 1 or 0 for each condition, then SUM() up the ones and zeros to retrieve a count.
SELECT
Value,
SUM(CASE WHEN Year = 2011 THEN 1 ELSE 0 END) AS 2012,
SUM(CASE WHEN Year = 2012 THEN 1 ELSE 0 END) AS 2012,
SUM(CASE WHEN Year = 2013 THEN 1 ELSE 0 END) AS 2013
FROM Table
GROUP BY Value

MySQL sum results into a table grid format

I have the following SQL code:
select distinct members_main.membership_type,
payment_method,sum(amount_paid)
from members_main, members_payments
where members_main.contact_id=members_payments.contact_id
group by members_main.membership_type,payment_method
That returns the following:
I want to be able to return the results in the following table grid format:
Does anyone know if or how I can do this within MySQL?
Thanks,
John
Depending on data complexity either go with bluefeet's way or if the payment_method amount is dynamic then it sounds like you need a pivot table. Try looking at this.
MySQL does not have a pivot function but you can use a CASE expression and an aggregate function to turn the rows into columns:
select m.membership_type,
sum(case when p.payment_method = 'Cash' then amount_paid else 0 end) Cash,
sum(case when p.payment_method = 'Credit Card' then amount_paid else 0 end) CreditCard,
sum(case when p.payment_method = 'Paypal' then amount_paid else 0 end) Paypal
from members_main m
inner join members_payments p
on m.contact_id=p.contact_id
group by m.membership_type;
If you are going to have an unknown number of payment_method's, then you will want to look at using a prepared statement to generate dynamic SQL:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(CASE WHEN p.payment_method = ',
payment_method,
' THEN amount_paid else 0 END) AS `',
payment_method, '`'
)
) INTO #sql
FROM members_payments ;
SET #sql
= CONCAT('SELECT m.membership_type, ', #sql, '
from members_main m
inner join members_payments p
on m.contact_id=p.contact_id
group by m.membership_type');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;