specific quarter dates in qlikview, - qlikview

I wanted to create quarter section but without the ceiling function because I have specific dates for each quarter.
first quarter is from the 22 of October to the 21 to January.
so I've created this function: (in the loadscript)
if((Month(RetDate)='10' AND Day(RetDate)>21) OR (Month(RetDate) = '11' OR Month(RetDate) = '12') OR (Month(RetDate)='1' AND Day(RetDate)<22),'Q1',
if((Month(RetDate)='1' AND Day(RetDate)>21) OR (Month(RetDate) = '2' OR Month(RetDate) = '3') OR (Month(RetDate)='4' AND Day(RetDate)<22),'Q2',
if((Month(RetDate)='4' AND Day(RetDate)>21) OR (Month(RetDate) = '5' OR Month(RetDate) = '3') OR (Month(RetDate)='6' AND Day(RetDate)<22),'Q3','Q4'))) as Quarter1,
But in this way, for example, 23.10.13 (23 OCT.) is related to Q1 of 2013 instead of Q1 of 2014.
thanks :)

Ceil( Month( If( if(Day(RetDate)<22,RetDate,QuarterStart(RetDate,1)) )/3) as Quarter
so if the date is after the 22 i push it a quarter forward.

I think this is what you are looking for:
LOAD Customer,
RetDate,
if(Month(RetDate)='1',
if(Day(RetDate)<22, 'Q1', 'Q2'),
if(Month(RetDate)='2', 'Q2',
if(Month(RetDate)='3', 'Q2',
if(Month(RetDate)='4',
if(Day(RetDate)<22, 'Q2', 'Q3'),
if(Month(RetDate)='5', 'Q3',
if(Month(RetDate)='6', 'Q3',
if(Month(RetDate)='7',
if(Day(RetDate)<22, 'Q3', 'Q4'),
if(Month(RetDate)='8', 'Q4',
if(Month(RetDate)='9', 'Q4',
if(Month(RetDate)='10',
if(Day(RetDate)<22, 'Q4', 'Q1'),
if(Month(RetDate)='11', 'Q1',
if(Month(RetDate)='12', 'Q1',
'undef' )))))))))))) as Quarter,
INLINE [
Customer, RetDate
A-Mark, 20.10.2013
A-Mark, 21.10.2013
A-Mark, 22.10.2013
A-Mark, 23.10.2013
A-Mark, 24.10.2013
C-Mart, 19.01.2014
C-Mart, 20.01.2014
C-Mart, 21.01.2014
C-Mart, 22.01.2014
]
and the result is:
Hope that helps.

Related

How to query previous 8 quarters for quarterly data report using sql server?

I want to query the previous 8 quarters from today's date.
Example: last quarter from today's date = '2020-09-30' and last 8 quarter from today's date is '2018-10-01'.
I want the last 8 quarter previous ('2018-10-01') mark as Q1 in my query result instead of Q4 since it's the 4th quarter of the year 2018. Q2 would be the next quarter which is from January-March 2019 and so on, so forth.
Is there a way to count it from the starting date to current date?
My current query:
SELECT
name,
Q1,
Q2,
Q3,
Q4,
Q5,
Q6,
Q7,
Q8,
FROM (SELECT distinct
s.custId,
sum(s.total) as total,
CAST('Q'+ Cast(DATEPART(QUARTER, s.purchaseDate)AS VARCHAR(1)) AS VARCHAR(2)) AS Quarterly,
c.name,
FROM sales s
LEFT OUTER JOIN customers c
ON c.id = s.custId
WHERE AND purchaseDate >= '2018-10-01'
AND purchaseDate <= '2020-09-30'
GROUP BY
s.custId) AS data
PIVOT ( SUM(total)
FOR quarterly IN ([Q1],
[Q2],
[Q3],
[Q4],
[Q5],
[Q6],
[Q7],
[Q8]) )AS pvt
ORDER by name
I would just use conditional aggregation:
SELECT s.custId, c.name,
SUM(CASE WHEN DATEDIFF(quarter, s.purchasedate, GETDATE()) = 8
THEN s.total
END) as q1,
SUM(CASE WHEN DATEDIFF(quarter, s.purchasedate, GETDATE()) = 7
THEN s.total
END) as q2,
. . .
FROM sales s LEFT OUTER JOIN
customers c
ON c.id = s.custId AND
WHERE purchaseDate >= '2018-10-01' AND
purchaseDate <= '2020-09-30'
GROUP BY s.custId, c.name;
for the "hard-coded" approach, you could define Quarterly as 9-quarterdiff(between purchaseDate&current date) [or as 9+quarterdiff(between current date&purchaseDate]
declare #sales table (purchaseDate date, total int);
insert into #sales(purchaseDate, total)
values
('20181016', 4), ('20181220', 5),
('20190219', 6),
('20190524', 11), ('20190620', 7),
('20190708', 12),
('20200210', 20),
('20200923', 19), ('20200926', 11),
('20201111', 2) --this is q9
;
--9&quarterdiff (when/if checking !!previous 8 quarters only!!)
select pvt.*
from
(
select total,
concat('Q', 9-datediff(quarter, purchaseDate, getdate())) as Quarterly
--concat('Q', 9+datediff(quarter, getdate(), purchaseDate)) as Quarterly
from #sales
where purchaseDate >= '20181001'
and purchaseDate <= '20200930' -- or maybe.. < '20201001', even if it's a date datatype
) as s
pivot
(
sum(total) for Quarterly in (Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8)
) as pvt;

Oracle SQL variable/parameter usage in query

I have this query below and I need to change WHERE conditions depending on QUARTER.
Meaning that I have to copy that query and change the DATE conditions to 06.2020 then use UNION.
I have no idea how can I optimize this query only with SQL because I am not able to DEFINE some variables/parameters in SQL (not using PL/SQL).
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('03.2020', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('03.2020', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('03.2020', 'MM.YYYY'))
Thanks for help
If SQL*Developer, or SQLPlus, you can use the 'substitution variables'.
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER -- << probably want TO_CHAR('&&1', 'Q YYYY') or similar here
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('&&1', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('&&1', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('&&1', 'MM.YYYY'))
When you run this it will prompt you for the value.
If you used just '&1' it would prompt you for each occurence.
'&&1' reuses the value of the first occurence.
You could specify different variables using &1, &2, ...
Also you may used named variable as follows:
To prompt for the date :
ACCEPT dt CHAR PROMPT 'ENter the date (MM.YYYY): '
Or setting the value at declaration:
DEFINE dt='03.2020'
Then:
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER -- << probably want TO_CHAR('&&dt', 'Q YYYY') or similar here
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('&&dt', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('&&dt', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('&&dt', 'MM.YYYY'))
Ok first of all a disclaimer:
As I know nothing about your original query's logic - the query I propose only shows the main logic - and probably will not run as is.
Having said that - the approach would be:
Based on parameters :start_y (start year) and :end_y (end year)
generate a list with all the quarters for those years years_and_quarters.
Join the years_and_quarters with the required table based on quarter start and end dates - and then calculate the sums grouping by year and quarter.
with quarters as
(
select 'Q1' quarter, '03' q_end_date, '01' q_start_date from dual
union
select 'Q2' quarter, '06' q_end_date,'04' q_start_date from dual
union
select 'Q3' quarter, '09' q_end_date,'07' q_start_date from dual
union
select 'Q4' quarter, '12' q_end_date,'10' q_start_date from dual
),
years as
(select :start_y + (level-1) as mydate
from dual
connect by level <= (:end_y - :start_y) +1
),
years_and_quarters as (
select * from years,quarters
order by mydate
),
accounts as (
SELECT a.limit_amount ,
b.balance_amount,
'LOAN' as TYPE, b.balance_date,a.account_open_date,a.account_close_date
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
)
select sum(accounts.limit_amount) as LIMIT,
sum(accounts.balance_amount) as OUTSTANDING,
years_and_quarters.quarter,
accounts.TYPE
from
years_and_quarters,accounts
where
trunc(balance_date) = last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY'))
and trunc(account_close_date) > last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY')) and trunc(account_open_date) <= last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY'))
group by years_and_quarters.mydate,years_and_quarters.quarter
How about just aggregate by the quarter?
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
to_char(balance_date, '"Q"Q YYYY') as quarter
FROM accounts a LEFT JOIN
account_balances b
ON a.account_key = b.account_key AND
b.balance_type_key = 16 AND
b.balance_date = LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH)
WHERE a.account_close_date > LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH) AND
a.account_open_date <= LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH)
GROUP BY TRUNC(balance_date, 'Q') ;
Your query seems focused on the last day of the quarter. This arrives at that by adding two months to the start and using LAST_DAY().

SQL loop, condition queries

I have table 'db' which has these columns:
sname(student name)
cname (course name)
year ( the year when student take)
qtr ( the quarter when student take) : W , S, F (W>S>F)
grade
What I want to do is that list the students that gpa has increased every quarter (previous grade < current gpa). Students may have gaps between quarters.
SELECT a.sname
FROM db a
WHERE EXISTS (SELECT *
FROM db b
WHERE a.sname = b.sname
AND a.year = b.year
AND a.qtr < b.qtr
AND a.grade > b.grade)
UNION
After union, I will do the case when a.year > b.year.
This is what I am doing right now - is it correct way to do it this way?
I have no idea how I can loop in SQL queries...
Any advise will be appreciate. Thank you
If you're using the current version of sqlite (3.25.X), this is really easy to do using the lag() window function. In the below, I'm using numeric values for grades instead of letters because it's much easier to work with, and assuming the following table definition (Based on your description) and sample rows in it:
CREATE TABLE IF NOT EXISTS grades(
sname TEXT NOT NULL
, year INTEGER NOT NULL
, qtr INTEGER NOT NULL
, cname TEXT NOT NULL
, grade NUMERIC
, PRIMARY KEY (sname, year, qtr, cname)
) WITHOUT ROWID;
INSERT INTO grades(sname, cname, year, qtr, grade) VALUES
('Bob', 'Math', 2017, 3, 2.0), ('Bob', 'Math', 2017, 4, 2.5),
('Bob', 'Math', 2018, 1, 3.0), ('Amy', 'Math', 2017, 3, 4.0),
('Amy', 'Math', 2017, 4, 3.5), ('Amy', 'Math', 2018, 1, 4.0),
('Bob', 'History', 2017, 3, 3.5), ('Bob', 'History', 2017, 4, 3.0),
('Bob', 'History', 2018, 1, 3.5), ('Amy', 'History', 2017, 3, 2.5),
('Amy', 'History', 2017, 4, 3.5), ('Amy', 'History', 2018, 1, 4.0);
I assume that when you said previous grade < current gpa you meant previous gpa to match with gpa has increased every quartely[sic].
First, a query that calculates each student's GPA for each quarter and includes the previous GPA as well in each row (Thanks to lag()):
SELECT sname, year, qtr
, avg(grade) AS gpa
, lag(avg(grade), 1, 0.0)
OVER (PARTITION BY sname ORDER BY year, qtr) AS prev_gpa
FROM grades
GROUP BY sname, year, qtr
ORDER BY sname, year, qtr;
This produces:
sname year qtr gpa prev_gpa
---------- ---------- ---------- ---------- ----------
Amy 2017 3 3.25 0.0
Amy 2017 4 3.5 3.25
Amy 2018 1 4.0 3.5
Bob 2017 3 2.75 0.0
Bob 2017 4 2.75 2.75
Bob 2018 1 3.25 2.75
As you can see, with this sample data, Amy has an always-increasing GPA, while Bob doesn't. So, the issue is how to filter the results to just her? The answer to that lies in using HAVING with a GROUP BY. It's complicated a bit because values computed by window functions can only appear in the select column list and ORDER BY clauses, so I shove the above query into a CTE to work around that restriction:
WITH gpas AS (
SELECT sname, year, qtr
, avg(grade) AS gpa
, lag(avg(grade), 1, 0.0)
OVER (PARTITION BY sname ORDER BY year, qtr) AS prev_gpa
FROM grades
GROUP BY sname, year, qtr)
SELECT sname
FROM gpas
GROUP BY sname
HAVING sum(CASE WHEN gpa > prev_gpa THEN 1 ELSE 0 END) = count(gpa)
ORDER BY sname;
which produces
sname
----------
Amy
The GROUP BY sname HAVING ... part filters out the students where at least one of their rows doesn't have an increased GPA compared to their previous one. It's worth spending some time reading up on grouping, as it's probably the most difficult basic concept to grasp and also very useful and powerful.

PIVOT with calculated aggregate function

I am trying to generate a few reports that display a calculated value for each month where the month values are the columns.
The base query works well to report the months as rows:
SELECT ROUND(SUM(REVENUE)/SUM(HEADCOUNT), 2), MONTH FROM TABLE
GROUP BY MONTH
But if I try to pivot the table, I consistently get the ORA-56902 error:' expect aggregate function inside pivot operation':
SELECT * FROM (
SELECT REVENUE, HEADCOUNT, MONTH FROM TABLE
)
PIVOT (ROUND(SUM(REVENUE)/SUM(HEADCOUNT), 2) FOR MONTH IN ('APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC', 'JAN', 'FEB', 'MAR'))
Is there any way to get PIVOT to see ROUND(SUM(REVENUE)/SUM(HEADCOUNT), 2) as an aggregate function, or is there some other function I should be using.
I would advise just using conditional aggregation:
SELECT ROUND(SUM(CASE WHEN MONTH = 'APR' THEN REVENUE END)/
SUM(CASE WHEN MONTH = 'APR' THEN HEADCOUNT END
), 2) as APR,
ROUND(SUM(CASE WHEN MONTH = 'MAY' THEN REVENUE END)/
SUM(CASE WHEN MONTH = 'MAY' THEN HEADCOUNT END
), 2) as MAY,
. . .
FROM TABLE;
I should point out that you can use pivot. Just calculate the summaries in the subquery and then pivot:
SELECT *
FROM (SELECT MONTH, ROUND(SUM(REVENUE) / SUM(HEADCOUNT), 2) as val
FROM TABLE
GROUP BY MONTH
) m
PIVOT (MAX(val)) FOR MONTH IN ('APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC', 'JAN', 'FEB', 'MAR'))
PIVOT requires an aggregate function, unconditionally.
One way is to calculate your value in the subquery and then do the pivoting.
select *
from (
select
round(sum(revenue)/sum(headcount), 2) val,
month
from table
group by month
)
pivot (
max(val)
for month in ('APR', 'MAY', 'JUN',
'JUL', 'AUG', 'SEP', 'OCT',
'NOV', 'DEC', 'JAN',
'FEB', 'MAR')
)

Aggregating Data in SQL

When looking at the picture in the link I need the Imploded Units to Average and the Exploded Units to Sum, I will attach my code below the Results shown below. I have been searching for an answer to this for a few days. I fear I may be trying to code over my head a little bit.
SELECT A.to_load_id AS "Cases",
A.from_qty - A.to_qty AS "Imploded Units",
( A.from_qty - A.to_qty ) * Nvl(SUM(p.qty), 1) AS "Exploded Units",
A.wskusku AS "Sku's",
A.wave AS "Wave",
A.from_loc AS "Processed Location",
A.free_form_text AS "Zone" ,
FROM audits A,
prepack P
WHERE A.from_loc LIKE 'S%'
AND A.to_loc = A.from_loc
AND free_form_text IN ( '01', '02', '03', '04',
'05', '06', '07', '08',
'09', '10', '11', '12',
'13', '14' )
AND A.wskusku = p.sku (+)
AND A.from_load_id = '42419472'
AND A.wave in ('WC055193','','','','','','','','','')
AND To_date(Substr(a.date_wms, 1, 12), 'YYYY/MM/DD HH24:MI') >=
SYSDATE - 4
GROUP BY A.to_load_id,
A.from_qty,
A.to_qty,
P.qty,
A.wskusku,
A.wave,
A.from_loc,
A.free_form_text
You need to use avg() / sum() and not GROUP BY columns that are involved in aggregates:
SELECT A.to_load_id AS "Cases"
,avg(A.from_qty - A.to_qty) AS "Imploded Units"
,sum(A.from_qty - A.to_qty) * Nvl(SUM(P.qty), 1) AS "Exploded Units"
,A.wskusku AS "Sku's"
,A.wave AS "Wave"
,A.from_loc AS "Processed Location"
,A.free_form_text AS "Zone"
FROM audits A
JOIN prepack P ON P.sku (+) = A.wskusku
WHERE A.from_loc LIKE 'S%'
AND A.to_loc = A.from_loc
AND A.free_form_text IN ( '01', '02', '03', '04',
'05', '06', '07', '08',
'09', '10', '11', '12',
'13', '14' )
AND A.from_load_id = '42419472'
AND A.wave in ('WC055193','','','','','','','','','')
AND To_date(Substr(A.date_wms, 1, 12), 'YYYY/MM/DD HH24:MI') >= SYSDATE - 4
GROUP BY A.to_load_id,
,A.wskusku
,A.wave
,A.from_loc
,A.free_form_text
Not sure why you multiple the "Exploded Units", but not the "Imploded Units". I copied what you have there.