This question already has answers here:
The new PIVOT function in BigQuery
(1 answer)
GBQ Transpose Table in SQL
(1 answer)
Pivot table in SQL with multiple columns
(1 answer)
Closed 1 year ago.
Following this question, I was trying to create a dynamic SQL statement with a pivot table and a CTE in Google BigQuery.
This worked straight of the box (with CTE and pivot):
with Produce AS (
SELECT 'Kale' as product, 51 as sales, 'Q1' as quarter UNION ALL
SELECT 'Kale', 23, 'Q2' UNION ALL
SELECT 'Kale', 45, 'Q3' UNION ALL
SELECT 'Kale', 3, 'Q4' UNION ALL
SELECT 'Apple', 77, 'Q1' UNION ALL
SELECT 'Apple', 0, 'Q2' UNION ALL
SELECT 'Apple', 25, 'Q3' UNION ALL
SELECT 'Apple', 2, 'Q4')
SELECT * FROM
(SELECT product, sales, quarter FROM Produce)
PIVOT(SUM(sales) FOR quarter IN ('Q1', 'Q2', 'Q3', 'Q4'))
Then I tried to modify it to include the dynamic SQL using execute immediate:
execute immediate(
"""
with Produce as (
SELECT 'Kale' as product, 51 as sales, 'Q1' as quarter UNION ALL
SELECT 'Kale', 23, 'Q2' UNION ALL
SELECT 'Kale', 45, 'Q3' UNION ALL
SELECT 'Kale', 3, 'Q4' UNION ALL
SELECT 'Apple', 77, 'Q1' UNION ALL
SELECT 'Apple', 0, 'Q2' UNION ALL
SELECT 'Apple', 25, 'Q3' UNION ALL
SELECT 'Apple', 2, 'Q4'
)
select '''
select *
from (select product, sales, quarter from Produce)
pivot(sum(sales) for quarter in ("''' || string_agg(distinct quarter, '", "' order by quarter) || '''"))
'''
from Produce
"""
);
My issue is, this above prints out the query instead of executing it.
How can I modify it to actually get it to execute and get me the sample results using the data in the CTE?
Related
I use two choices to make monthly statistics. The first select selects the appropriate data, and then carefully counts the monthly statistics.
First select:
SELECT
TO_CHAR (ZDZDATETIME, 'RRRR-MM-DD') AS DA,
ID,
MAX (DAYRF) AS DAYRF
FROM
JC_T_QXJYJCSJ
WHERE
ID = 'G2001'
GROUP BY
TO_CHAR (ZDZDATETIME, 'RRRR-MM-DD'),
ID
ORDER BY
ID,
TO_CHAR (ZDZDATETIME, 'RRRR-MM-DD')
the result:
result
the final code:
SELECT
TO_CHAR (DA, 'RRRR-MM') AS DA1,
SUM (DAYRF)FROM(
SELECT
TO_CHAR (ZDZDATETIME, 'RRRR-MM-DD') AS DA,
ID,
MAX (DAYRF) AS DAYRF
FROM
JC_T_QXJYJCSJ
WHERE
ID = 'G2001'
GROUP BY
TO_CHAR (ZDZDATETIME, 'RRRR-MM-DD'),
ID
ORDER BY
ID,
TO_CHAR (ZDZDATETIME, 'RRRR-MM-DD')
) GROUP BY
TO_CHAR (DA, 'RRRR-MM');
Always prompt me for invalid numbers
I think the ID=‘G2001’ sentence has a problem, so I tried to change it to other ID='XXX' and it will run successfully. I don’t know if an error other than code has occurred.
Your question did not reveal much information about your issue.
I tried to convert your question into one problem statement and resolved the same using following query.
WITH DATAA(ID, DAYRF, DATE1) AS
(
SELECT 1, 10, DATE '2019-06-22' FROM DUAL UNION ALL
SELECT 1, 9 , DATE '2019-06-22' FROM DUAL UNION ALL
SELECT 1, 5, DATE '2019-06-22' FROM DUAL UNION ALL
SELECT 2, 20, DATE '2019-06-22' FROM DUAL UNION ALL
SELECT 2, 15, DATE '2019-06-22' FROM DUAL UNION ALL
SELECT 2, 12, DATE '2019-06-22' FROM DUAL UNION ALL
SELECT 3, 5, DATE '2019-06-22' FROM DUAL UNION ALL
SELECT 3, 30, DATE '2019-06-22' FROM DUAL UNION ALL
SELECT 1, 20, DATE '2019-06-23' FROM DUAL UNION ALL
SELECT 1, 29 , DATE '2019-06-23' FROM DUAL UNION ALL
SELECT 1, 15, DATE '2019-06-23' FROM DUAL UNION ALL
SELECT 2, 40, DATE '2019-06-23' FROM DUAL UNION ALL
SELECT 2, 45, DATE '2019-06-23' FROM DUAL UNION ALL
SELECT 2, 32, DATE '2019-06-23' FROM DUAL UNION ALL
SELECT 3, 19, DATE '2019-06-23' FROM DUAL UNION ALL
SELECT 3, 1, DATE '2019-06-23' FROM DUAL
)
SELECT
DATE1,
SUM(MAX_DAYRF)
FROM
(
SELECT
DATE1,
ID,
MAX(DAYRF) AS MAX_DAYRF
FROM
DATAA
GROUP BY
DATE1,
ID
)
GROUP BY
DATE1;
Ouptut
Fiddle Demo
Cheers!!
I am running the below query to fetch monthly data count for last year till current month.
SELECT dateName(month,tn.processstarttime) as reqMonth, count(ID) as requestCount, year(tn.processstarttime) as reqYear
FROM table A tn WHERE year(tn.processstarttime) in (year(DATEADD(MONTH, 0, GETDATE())),year(DATEADD(MONTH, -12, GETDATE())))
AND tn.processstarttime>DATEADD(MONTH, -12, GETDATE())
GROUP BY dateName(month,tn.processstarttime),year(tn.processstarttime)
order by dateName(month,tn.processstarttime),year(tn.processstarttime)
But this query is not giving month names for which the data count is 0.
Please support to include months for which the data count is 0 with value as 0.
Thanks
The standard way is to use calendar table with all years nad month required and then LEFT JOIN to it your result. When there will be no corresponding record in your table to some meonth, you will use COALESCE to obtain 0 for those months. See below query (I used CTEs to get calendar table, IMO the easiest way):
;with MonthNames as (
select 1 MonthNo, 'January' MonthName
union all
select 2, 'February'
union all
select 3, 'March'
union all
select 4, 'April'
union all
select 5, 'May'
union all
select 6, 'June'
union all
select 7, 'July'
union all
select 8, 'August'
union all
select 9, 'September'
union all
select 10, 'October'
union all
select 11, 'November'
union all
select 12, 'December'
), Years as (
select 2017 Year union all select 2018 union all select 2019
), CalendarTable as (
select * from MonthNames cross join Years
)
select ct.MonthName,
ct.Year,
COALESCE(t.requestCount, 0) requestCount
from CalendarTable ct
left join (YOUR WHOLE SELECT) t
on t.Year = ct.Year and t.month = ct.MonthNo
This answer can be similar to Michal Turczyn's, but there are a couple of substantial differences:
Do not pay much attention on the differences creating the first two CTEs, as different they look as irrelevant, simply matter of styles.
The important difference is in the third CTE and the way of filter your query, the name of your column (processstarttime) is giving a clue that it can be a very large table, so if you use where clauses using functions for the selected table columns, it will work, but your query won't be indexed and the performance can be a further issue
Not top relevant but also important is that It cover the "monthly data count for last year till current month" PO's requirement without hardcoding dates, it can be within a view or function that doesn't need to be modifiied year by year...
WITH months AS (
SELECT 1 AS MonthNum, DATENAME(Month,DATEFROMPARTS(1,1,1)) AS MonthName
UNION ALL
SELECT MonthNum + 1, DATENAME(Month,DATEFROMPARTS(1, MonthNum + 1, 1)) AS MonthName
FROM months
WHERE MonthNum <= 11
),
years as (
SELECT YEAR(GETDATE())-1 AS Year
UNION ALL
SELECT Year + 1
FROM years
WHERE Year + 1 <= YEAR(GETDATE())
),
dates as (
SELECT Year, MonthNum, MonthName, DATEFROMPARTS(Year, MonthNum, 1) AS DateStart, DATEADD(MONTH, 1, DATEFROMPARTS(Year, MonthNum, 1)) AS DateEnd
FROM years
CROSS JOIN months
)
SELECT D.Year, D.MonthNum, D.MonthName, COUNT(ID) AS RequesCount
FROM dates D
LEFT JOIN YourTable A ON A.ProcessStartTime >= DateStart AND A.ProcessStartTime < DateEnd
WHERE DateStart < GETDATE()
GROUP BY D.Year, D.MonthNum, D.MonthName
ORDER BY Year, MonthNum
Try this
SELECT months.month_name AS reqMonth, COUNT(ID) AS requestCount, YEAR(tn.processstarttime) AS reqYear
FROM A tn
RIGHT JOIN (VALUES ('january'),('february'),('march'),('april'),
('may'),('june'),('july'),('august'),('september'),
('october'),('november'),('december')) as months(month_name)
ON DATENAME(month,tn.processstarttime) = months.month_name
AND YEAR(tn.processstarttime) in (YEAR(DATEADD(MONTH, 0, GETDATE())),year(DATEADD(MONTH, -12, GETDATE())))
AND tn.processstarttime>DATEADD(MONTH, -12, GETDATE())
GROUP BY months.month_name,YEAR(tn.processstarttime)
order by months.month_name,YEAR(tn.processstarttime)
In action here
So I am trying to convert some Day of the Year dates to Date format. The problem is some day of the year dates are in DDYYYY format for days under 100 and DDDYYYY format for days 100 and over. I have tried the following but still receive a "day of year must be between 1 and 365 (366 for leap year)" error:
select CASE when data_Date >= 999999
then to_date(data_date, 'DDDYYYY')
when data_Date >= 99999
then to_date(data_Date, 'DDYYYY')
else to_date(data_date, 'DYYYY')
END as DATA_DATE_CONVERTED
from table;
Thanks in advance
Sample Data is as follows:
Data_date (being passed in as a varchar2)
1072015
12017
612014
672013
72017
1112018
The last 4 digit is the year. Use LPAD to put leading zeroes on DAY
select to_date( lpad(dayyear, 7, '0'),'DDDYYYY')
from table;
sqlfiddle
How about this:
with demo (data_date) as
( select 1072015 from dual union all
select 12017 from dual union all
select 612014 from dual union all
select 672013 from dual union all
select 72017 from dual union all
select 1112018 from dual )
select data_date
, to_char(data_date,'0000000')
, to_date(to_char(data_date,'0000000'),'DDDYYYY') as data_date_converted
from demo
You can also put a dash (-) between.
with demo (data_date) as
( select 1072015 from dual union all
select 12017 from dual union all
select 612014 from dual union all
select 672013 from dual union all
select 72017 from dual union all
select 1112018 from dual )
select data_date, TO_DATE(SUBSTR(data_date, 1, length(data_date)-4)||'-'||SUBSTR(data_date, -4, 4), 'DDD-YYYY')
from demo;
I have a following query:
SELECT '-SELECT MONTH' NAME, -1 VALUE
UNION ALL SELECT 'JANUARY' NAME,1 VALUE
UNION ALL SELECT 'FEBRUARY' NAME,2 VALUE
UNION ALL SELECT 'MARCH' NAME,3 VALUE
UNION ALL SELECT 'APRIL' NAME,4 VALUE
UNION ALL SELECT 'MAY' NAME,5 VALUE
UNION ALL SELECT 'JUNE' NAME,6 VALUE
UNION ALL SELECT 'JULY' NAME,7 VALUE
UNION ALL SELECT 'AUGUST' NAME,8 VALUE
UNION ALL SELECT 'SEPTEMBER' NAME,9 VALUE
UNION ALL SELECT 'OCTOBER' NAME,10 VALUE
UNION ALL SELECT 'NOVEMBER' NAME,11 VALUE
UNION ALL SELECT 'DECEMBER' NAME,12 VALUE
which I bind to a dropdown.
I have another query
SELECT MONTH FROM HRMONTHYEARMASTER
WHERE LOCKINGSTATUS IS NULL OR LOCKINGSTATUS <> '05'
which gives me only particular months.
My problem is that i want to combine the two queries and get only the name of the month and its value in my resultset.
I dont know how to go about it. Please help..
For union you must have the same coulmn name :
SELECT '-SELECT MONTH' NAME, -1 VALUE
UNION ALL SELECT 'JANUARY' NAME,1 VALUE
UNION ALL SELECT 'FEBRUARY' NAME,2 VALUE
UNION ALL SELECT 'MARCH' NAME,3 VALUE
UNION ALL SELECT 'APRIL' NAME,4 VALUE
UNION ALL SELECT 'MAY' NAME,5 VALUE
UNION ALL SELECT 'JUNE' NAME,6 VALUE
UNION ALL SELECT 'JULY' NAME,7 VALUE
UNION ALL SELECT 'AUGUST' NAME,8 VALUE
UNION ALL SELECT 'SEPTEMBER' NAME,9 VALUE
UNION ALL SELECT 'OCTOBER' NAME,10 VALUE
UNION ALL SELECT 'NOVEMBER' NAME,11 VALUE
UNION ALL SELECT 'DECEMBER' NAME,12 VALUE
UNION ALL
SELECT MONTH AS NAME, 100 AS VALUE FROM HRMONTHYEARMASTER
WHERE LOCKINGSTATUS IS NULL OR LOCKINGSTATUS <> '05'
SELECT MONTH VALUE,DATENAME(month, DATEADD(month, MONTH-1, CAST('2008-01-01' AS datetime))) NAME
FROM HRMONTHYEARMASTER
WHERE LOCKINGSTATUS IS NULL OR LOCKINGSTATUS <> '05'
This solved my problem. Now I get the Name as well as the int value of a month.
Thanks Guys!!!!
I have a view which contains the data seen in the image below.
The view is showing me how many working days are available in each month for the current financial year taking away any school/bank holidays.
As the month of August has zero days available it has excluded this month from the view.
As the total number of days available will always be zero for the month of August, then it seems acceptable to hardcode the SQL to always have 0 for August, and also an April-August record which will be the same as April-July.
What would be the best way to add these 2 records, and where about in the code should it be placed see example of code layout:
see link (answered question) for layout of code:
SQL populate total working days per month minus bank holidays for current financial year
For my answer, I will assume you have a view vDays with columns that match your screen shot: period, availabledays, year.
To append any zero-day periods to your results whatever month may have zero (which will cater for August and any other month that happens to have zero days), you can extend your view like this:
WITH Mths (Mth) AS (
SELECT 'January'
UNION SELECT 'February'
UNION SELECT 'March'
UNION SELECT 'April'
UNION SELECT 'May'
UNION SELECT 'June'
UNION SELECT 'July'
UNION SELECT 'August'
UNION SELECT 'September'
UNION SELECT 'October'
UNION SELECT 'November'
UNION SELECT 'December'
UNION SELECT 'April - January'
UNION SELECT 'April - February'
UNION SELECT 'April - March'
UNION SELECT 'April - May'
UNION SELECT 'April - June'
UNION SELECT 'April - July'
UNION SELECT 'April - August'
UNION SELECT 'April - September'
UNION SELECT 'April - October'
UNION SELECT 'April - November'
UNION SELECT 'April - December'
), Years (Year) AS (
SELECT DISTINCT year
FROM vDays
), ZeroPeriods (Mth, Years) AS (
SELECT Mth, Year
FROM Mths, Years
), JoinedData (Mth, AvailableDays, Year) AS (
SELECT Mth, 0, Years
FROM ZeroPeriods
UNION ALL
SELECT period, availabledays, year
FROM vDays
), GroupedData (Mth, AvailableDays, Year) AS (
SELECT Mth, SUM(AvailableDays), Year
FROM JoinedData
GROUP BY Mth, Year
)
SELECT *
FROM GroupedData
ORDER BY Year, CASE UPPER(LEFT(Mth, 3))
WHEN 'JAN' THEN 1 WHEN 'FEB' THEN 2 WHEN 'MAR' THEN 3
WHEN 'APR' THEN 4 WHEN 'MAY' THEN 5 WHEN 'JUN' THEN 6
WHEN 'JUL' THEN 7 WHEN 'AUG' THEN 8 WHEN 'SEP' THEN 9
WHEN 'OCT' THEN 10 WHEN 'NOV' THEN 11 ELSE 12 END;
I have split this out into lots of separate queries, although some could be merged into sub queries, but doing it like this makes it a lot clearer to understand.
Would this give you your desired result set?
SELECT Period, DaysAvailable, Year FROM YOURVIEW
UNION ALL
SELECT DISTINCT 'April-August', DaysAvailable, Year FROM YOURVIEW where Period = 'April-July'
UNION ALL
SELECT DISTINCT 'August', 0, YEAR FROM YOURVIEW