I want to see user statics, so I made query:
SELECT l.partner AS Partner ,
bu.meno||' '||decode(substr(bu.priezvisko, 1, 2), 'Sz',
substr(bu.priezvisko, 1, 2), 'Gy',
substr(bu.priezvisko, 1, 2), 'Ny',
substr(bu.priezvisko, 1, 2), 'Zs',
substr(bu.priezvisko, 1, 2), 'Cs',
substr(bu.priezvisko, 1, 2),
substr(bu.priezvisko, 1, 1))
||'.' AS prod_man --hungarian names have 2letter (surname)
, SUM(CASE
WHEN o.pocet!=0 THEN 1
ELSE 0
END) AS obj_pocet -- counting items
, SUM(CASE
WHEN o.pocet=0 OR o.p_del+o.p_del_dod>=o.pocet THEN 1
ELSE 0
END) AS nedod_pocet -- counting items2
, ROUND(SUM(CASE
WHEN o.pocet=0 OR o.p_del+o.p_del_dod>=o.pocet THEN 1
ELSE 0
END)/count(*), 3) * 100 AS "%" --percentage
FROM obj_odb_o o
JOIN obj_odb_l l ON o.rid_o=l.rid
JOIN sklad_karta sk ON sk.id=o.kod_id
JOIN bartex_users bu ON bu.id=sk.id.prod_man
WHERE l.partner in (325,
326)
GROUP BY l.partner
, bu.meno||' '||decode(substr(bu.priezvisko, 1, 2), 'Sz',
substr(bu.priezvisko, 1, 2), 'Gy',
substr(bu.priezvisko, 1, 2), 'Ny',
substr(bu.priezvisko, 1, 2), 'Zs',
substr(bu.priezvisko, 1, 2), 'Cs',
substr(bu.priezvisko, 1, 2),
substr(bu.priezvisko, 1, 1))
||'.'
It's working. Here is the result:
But I want to make a pivot by Months (last 6 months)...
WITH MONTHS AS
(
SELECT ADD_MONTHS(TRUNC(SYSDATE,'MONTH'),-LEVEL+1) AS MONTH,
DECODE(LEVEL,1,'Akt_mesiac','minuly_mesiac'||(LEVEL-1)) AS MONTH_NAME FROM DUAL CONNECT BY LEVEL <=7)
SELECT
partner,
prod_man,
'%',
NVL(Akt_mesiac,0) AS Akt_mesiac,
NVL(minuly_mesiac1,0) AS minuly_mesiac1,
NVL(minuly_mesiac2,0) AS minuly_mesiac2,
NVL(minuly_mesiac3,0) AS minuly_mesiac3,
NVL(minuly_mesiac4,0) AS minuly_mesiac4,
NVL(minuly_mesiac5,0) AS minuly_mesiac5,
NVL(minuly_mesiac6,0) AS minuly_mesiac6
FROM (
SELECT
-- my query - HERE I HAVE PROBLEM HERE
FROM MONTHS M
JOIN obj_odb_l l ON M.MONTH=TRUNC(l.datum_p,'MONTH')
) PIVOT
( SUM(CNT)
FOR MONTH_NAME IN
('Akt_mesiac' AS Akt_mesiac,
'minuly_mesiac1' AS minuly_mesiac1,
'minuly_mesiac2' AS minuly_mesiac2,
'minuly_mesiac3' AS minuly_mesiac3,
'minuly_mesiac4' AS minuly_mesiac4,
'minuly_mesiac5' AS minuly_mesiac5,
'minuly_mesiac6' AS minuly_mesiac6)
);
Table: obj_odb_l l ->date column -> l.datum_p -> trunc(l.datum_p,'MONTH')
How can I make a pivot table ?
Consider adding the month expression, TRUNC(l.datum_p,'MONTH'), into above aggregate query. Then run the query as another CTE in pivot query for JOIN in pivot's data source.
WITH MONTHS AS (
SELECT ADD_MONTHS(TRUNC(SYSDATE,'MONTH'),-LEVEL+1) AS MONTH
, DECODE(LEVEL,1,'Akt_mesiac','minuly_mesiac'||(LEVEL-1)) AS MONTH_NAME
FROM DUAL CONNECT BY LEVEL <=7
)
, AGG AS (
-- SAME AGGREGATE QUERY WITH TRUNC(l.datum_p,'MONTH') ADDED TO SELECT AND GROUP BY
-- POSSIBLY ADD WHERE CONDITION FOR LAST SIX MONTHS (IF DATA GOES BACK YEARS)
)
SELECT *
FROM (
SELECT AGG.partner
, AGG.prod_man
, AGG.obj_pocet
, AGG.nedod_pocet
, AGG.'%' AS PCT -- AVOID SPECIAL CHARS AS NAME
, M.MONTH_NAME
FROM MONTHS M
INNER JOIN AGG
ON M.MONTH = AGG.MONTH -- NEW FIELD USED FOR JOIN
)
PIVOT
( SUM(PCT) -- ONLY PIVOTS ONE NUM AT A TIME
FOR MONTH_NAME IN
('Akt_mesiac' AS Akt_mesiac,
'minuly_mesiac1' AS minuly_mesiac1,
'minuly_mesiac2' AS minuly_mesiac2,
'minuly_mesiac3' AS minuly_mesiac3,
'minuly_mesiac4' AS minuly_mesiac4,
'minuly_mesiac5' AS minuly_mesiac5,
'minuly_mesiac6' AS minuly_mesiac6)
);
Related
I have this Table:
Table_1:
KeyValue, Month
1, Oct,
3, Nov,
4, Sep,
5, Jan
upto "December". I want this tale to use as dictionary for another table query. So, if i am giving a Case query, where i am getting the Month of a date than this month should be replaced by the KeyValue of Table_1. For example:
CAST(Month(getdate()) as int)
gives "Sep" but the output should be
"4" because it is KeyValues of 'Sep' in Table_1
How it can be achieved in Snowflake?
It is just a subquery that perfrom lookup in dictionary table:
SELECT *, (SELECT t1.KeyValue FROM Table_1 AS t1 WHERE t1.Month = MONTH(GETDATE()))
FROM some_tab;
Also possible to implement as JOIN:
SELECT s.*, t1.KeyValue
FROM some_tab AS s
JOIN Table_1 AS t1
ON t1.Month = s.<col_name>;
I am trying to understand what is so complex here when you can achieve it with a simple join.
Consider month_mapping as your mapping table with key-value pair, and dataset as another table that you want to join with
with month_mapping (month_key, month_value) as
(
select * from
(
values (1, 'Oct')
, (3, 'Nov')
, (4, 'Sep')
, (5, 'Jan')
)
)
, dataset (actual_date) as
(
select * from
values (current_timestamp)
, (dateadd(month, 1, current_timestamp))
, (dateadd(month, 2, current_timestamp))
, (dateadd(month, 4, current_timestamp))
, (dateadd(day, 2, current_timestamp))
, (dateadd(day, -10, current_timestamp))
, (dateadd(day, 20, current_timestamp))
)
select ds.actual_date, mm.month_key, mm.month_value
from dataset ds
inner join month_mapping mm
on upper(mm.month_value) = upper(monthname(ds.actual_date))
I have this select query, that summarize columns:
SELECT
SUM (LineTotal),
SUM (VatSum),
SUM (GTotal)
FROM CSI1
WHERE DocEntry = '100'
It shows the expected values correctly, but I need to display the id (DocEntry) value too.
Thanks for any advice!
You need a GROUP BY clause:
SELECT
DocEntry,
SUM(LineTotal) AS LineTotal,
SUM(VatSum) AS VatSum,
SUM(GTotal) AS GTotal
FROM (VALUES
('100', 1, 1, 1),
('100', 1, 1, 1),
('200', 1, 1, 1)
) CSI1 (DocEntry, LineTotal, VatSum, GTotal)
WHERE DocEntry = '100'
GROUP BY DocEntry
Result:
DocEntry LineTotal VatSum GTotal
--------------------------------
100 2 2 2
I am trying to find all records that exist within a date range prior to an event occurring. In my table below, I want to pull all records that are 3 days or less from when the switch field changes from 0 to 1, ordered by date, partitioned by product. My solution does not work, it includes the first record when it should skip as it's outside the 3 day window. I am scanning a table with millions of records, is there a way to reduce the complexity/cost while maintaining my desired results?
http://sqlfiddle.com/#!18/eebe7
CREATE TABLE productlist
([product] varchar(13), [switch] int, [switchday] date)
;
INSERT INTO productlist
([product], [switch], [switchday])
VALUES
('a', 0, '2019-12-28'),
('a', 0, '2020-01-02'),
('a', 1, '2020-01-03'),
('a', 0, '2020-01-06'),
('a', 0, '2020-01-07'),
('a', 1, '2020-01-09'),
('a', 1, '2020-01-10'),
('a', 1, '2020-01-11'),
('b', 1, '2020-01-01'),
('b', 0, '2020-01-02'),
('b', 0, '2020-01-03'),
('b', 1, '2020-01-04')
;
my solution:
with switches as (
SELECT
*,
case when lead(switch) over (partition by product order by switchday)=1
and switch=0 then 'first day switch'
else null end as leadswitch
from productlist
),
switchdays as (
select * from switches
where leadswitch='first day switch'
)
select pl.*
,'lead'
from productlist pl
left join switchdays ss
on pl.product=ss.product
and pl.switchday = ss.switchday
and datediff(day, pl.switchday, ss.switchday)<=3
where pl.switch=0
desired output, capturing records that occur within 3 days of a switch going from 0 to 1, for each product, ordered by date:
product switch switchday
a 0 2020-01-02 lead
a 0 2020-01-06 lead
a 0 2020-01-07 lead
b 0 2020-01-02 lead
b 0 2020-01-03 lead
If I understand correctly, you can just use lead() twice:
select pl.*
from (select pl.*,
lead(switch) over (partition by product order by switchday) as next_switch_1,
lead(switch, 2) over (partition by product order by switchday) as next_switch_2
from productlist pl
) pl
where switch = 0 and
1 in (next_switch_1, next_switch_2);
Here is a db<>fiddle.
EDIT (based on comment):
select pl.*
from (select pl.*,
min(case when switch = 1 then switchdate end) over (partition by product order by switchdate desc) as next_switch_1_day
from productlist pl
) pl
where switch = 0 and
next_switch_one_day <= dateadd(day, 2, switchdate);
I am trying to display last 60 minutes statistics on pentaho dashboard (oracle 11g query passing to pentaho ).
I have column (counter_buff) in my table with 1000 counter positions with sample data shown below
counter_buff= '0,8,9,3,2,6,....15,62' up to 1000 comma seperated values
i am trying to fetch each comma separated values from table as per fixed positions provided and sum them, so the problem is if i use multiple positions query gets bigger & slower, slower query result in delayed statistics on dashboard.
I created this sample query & result:
Query:
the Numbers showing in {} is a counter positions({16},{24}..), this positions will be user defined. query also using 6 union all same like this.
select * from
((SELECT MIN(to_char(TIMESTAMP,'HH24:MI:SS')) as TS,
'SELL' as "STATUS",
SUM((regexp_substr(counter_buff,'(.*?,){16}(.*?),', 1, 1,'', 2)) +
(regexp_substr(counter_buff,'(.*?,){24}(.*?),', 1, 1,'', 2)) +
(regexp_substr(counter_buff,'(.*?,){32}(.*?),', 1, 1,'', 2)) ......+
(regexp_substr(counter_buff,'(.*?,){168}(.*?),', 1, 1,'', 2))) AS "COUNTS"
FROM (SELECT * FROM SHOPS
order by TO_CHAR("TIMESTAMP",'YYYY-MM-DD HH24:MI:SS') desc) "SHOPS"
where TOY_NAME = 'LION'
and rownum <=60
GROUP BY TO_CHAR("TIMESTAMP",'HH24:MI'))
UNION ALL
(SELECT MIN(to_char(TIMESTAMP,'HH24:MI:SS')) as TS,
'RETURNED' as "STATUS",
SUM((regexp_substr(counter_buff,'(.*?,){17}(.*?),', 1, 1,'', 2)) +
(regexp_substr(counter_buff,'(.*?,){25}(.*?),', 1, 1,'', 2)) ..... +
(regexp_substr(counter_buff,'(.*?,){153}(.*?),', 1, 1,'', 2)) +
(regexp_substr(counter_buff,'(.*?,){161}(.*?),', 1, 1,'', 2)) +
(regexp_substr(counter_buff,'(.*?,){169}(.*?),', 1, 1,'', 2))) AS "COUNTS"
FROM (SELECT * FROM SHOPS
order by TO_CHAR("TIMESTAMP",'YYYY-MM-DD HH24:MI:SS') desc) "SHOPS"
where TOY_NAME = 'LION'
and rownum <=60
GROUP BY TO_CHAR("TIMESTAMP",'HH24:MI')) )
order by TS desc,STATUS desc
result:
this is just some rows of result, result will be as per query rowid (to reduce space i pasted half result only , but i am using data of 60 last minutes)
TS STATUS COUNTS
10:20:01 SELL 6
10:21:01 SELL 9
10:22:01 SELL 8
10:23:01 SELL 3
10:20:01 RETURNED 1
10:21:01 RETURNED 6
10:22:01 RETURNED 7
10:23:01 RETURNED 2
I am able to achieve my desired output, But i want faster & smaller query option.
I am new to oracle query
You should filter data as much as possible at first, then make rest of job. Also union is not needed, you can do everything in one grouping, then only unpivot result if needed.
Below two queries, which should be useful. In first you have to write regexp_substr as many times as needed:
/* sample data
with shops(toy_name, time_stamp, counter_buff) as (
select 'LION', timestamp '2018-07-27 13:15:27', '0,8,9,3,2,6,15,62' from dual union all
select 'BEAR', timestamp '2018-07-27 13:44:06', '7,3,9,3,3,6,11,39' from dual union all
select 'LION', timestamp '2018-07-27 16:03:09', '7,3,151,44,3,6,11,39' from dual union all
select 'LION', timestamp '2018-07-27 16:03:49', '7,3,11,4,3,6,11,39' from dual )
-- end of data */
select to_char(time_stamp, 'hh24:mi') ts,
sum(regexp_substr(counter_buff,'(.*?,){2}(.*?),', 1, 1,'', 2) +
regexp_substr(counter_buff,'(.*?,){5}(.*?),', 1, 1,'', 2)) sell,
sum(regexp_substr(counter_buff,'(.*?,){3}(.*?),', 1, 1,'', 2) +
regexp_substr(counter_buff,'(.*?,){6}(.*?),', 1, 1,'', 2)) retu
from (select time_stamp, counter_buff, row_number() over (order by time_stamp desc) rn
from shops where toy_name = 'LION') t
where rn <= 60
group by to_char(time_stamp, 'hh24:mi')
In second I join two tables of predefined numbers with your data. These are the "user defined positions" used next as parameter for regexp_substr.
with
/* sample data
shops(toy_name, time_stamp, counter_buff) as (
select 'LION', timestamp '2018-07-27 13:15:27', '0,8,9,3,2,6,15,62' from dual union all
select 'BEAR', timestamp '2018-07-27 13:44:06', '7,3,9,3,3,6,11,39' from dual union all
select 'LION', timestamp '2018-07-27 16:03:09', '7,3,151,44,3,6,11,39' from dual union all
select 'LION', timestamp '2018-07-27 16:03:49', '7,3,11,4,3,6,11,39' from dual ),
*/ -- end of sample data
sell as (select rownum rn, column_value cs from table(sys.odcinumberlist(2, 5)) ),
retu as (select rownum rn, column_value cr from table(sys.odcinumberlist(3, 6)) )
select *
from (
select sum(regexp_substr(counter_buff,'(.*?,){'||cs||'}(.*?),', 1, 1,'', 2)) sell,
sum(regexp_substr(counter_buff,'(.*?,){'||cr||'}(.*?),', 1, 1,'', 2)) retu, ts
from (select to_char(time_stamp, 'HH24:MI') ts, counter_buff
from (select * from shops where toy_name = 'LION' order by time_stamp desc)
where rownum <= 60)
cross join sell join retu using (rn) group by ts)
unpivot (val for status in (sell, retu))
In both queries I assumed that sell is in positions (2, 5), returned in positions (3, 6). Also try row_number() against rownum and check which is faster for you. In both cases data is hit only once, this should speed up calculations.
At work, one of my assignments is to calculate commission to the sales staff. One rule has been more challenging than the others.
Two sales teams A and B work together each selling different products. Team A can send leads to team B. The same customer might be send multiple times. The first time a customer (ex. lead 1)* is send a commission is paid to the salesperson in team A who created the lead. Now the customer is “locked” for the next 365 days (counting from the date lead 1 was created). Meaning that no one can get additional commission for that customer in that period by sending additional leads (ex. Lead 2 and 3 gets no commission). After the 365 days have expired. A new lead can be created and receive commission (ex. Lead 4). Then the customer is locked again for 365 days counting from the day lead 4 was created. Therefore, lead 5 gets no commission. The tricky part is to reset the date that the 365 days is counted from.
'* Reference to tables #LEADS and #DISERED result.
I have solved the problem in tSQL using a cursor, but I wonder if it was possible to use a recursive CTE instead. I have made several attempts the best one is pasted in below. The problem with my solution is, that I refer to the recursive table more than once. I have tried to fix that problem with nesting a CTE inside a CTE. That’s is not allowed. I have tried using a temporary table inside the CTE that is not allowed either. I tried several times to recode the recursive part of the CTE so that the recursive table is referenced only once, but then I am not able to get the logic to work.
I am using SQL 2008
IF OBJECT_ID('tempdb.dbo.#LEADS', 'U') IS NOT NULL
DROP TABLE #LEADS;
CREATE TABLE #LEADS (LEAD_ID INT, CUSTOMER_ID INT, LEAD_CREATED_DATE DATETIME, SALESPERSON_NAME varchar(20))
INSERT INTO #LEADS
VALUES (1, 1, '2013-09-01', 'Rasmus')
,(2, 1, '2013-11-01', 'Christian')
,(3, 1, '2014-01-01', 'Nadja')
,(4, 1, '2014-12-24', 'Roar')
,(5, 1, '2015-12-01', 'Kristian')
,(6, 2, '2014-01-05', 'Knud')
,(7, 2, '2015-01-02', 'Rasmus')
,(8, 2, '2015-01-08', 'Roar')
,(9, 2, '2016-02-05', 'Kristian')
,(10, 2, '2016-03-05', 'Casper')
SELECT *
FROM #LEADS;
IF OBJECT_ID('tempdb.dbo.#DISERED_RESULT', 'U') IS NOT NULL
DROP TABLE #DISERED_RESULT;
CREATE TABLE #DISERED_RESULT (LEAD_ID INT, DESIRED_COMMISION_RESULT CHAR(3))
INSERT INTO #DISERED_RESULT
VALUES (1, 'YES')
,(2, 'NO')
,(3, 'NO')
,(4, 'YES')
,(5, 'NO')
,(6, 'YES')
,(7, 'NO')
,(8, 'YES')
,(9, 'YES')
,(10, 'NO')
SELECT *
FROM #DISERED_RESULT;
WITH COMMISSION_CALCULATION AS
(
SELECT T1.*
,COMMISSION = 'YES'
,MIN_LEAD_CREATED_DATE AS COMMISSION_DATE
FROM #LEADS AS T1
INNER JOIN (
SELECT A.CUSTOMER_ID
,MIN(A.LEAD_CREATED_DATE) AS MIN_LEAD_CREATED_DATE
FROM #LEADS AS A
GROUP BY A.CUSTOMER_ID
) AS T2 ON T1.CUSTOMER_ID = T2.CUSTOMER_ID AND T1.LEAD_CREATED_DATE = T2.MIN_LEAD_CREATED_DATE
UNION ALL
SELECT T10.LEAD_ID
,T10.CUSTOMER_ID
,T10.LEAD_CREATED_DATE
,T10.SALESPERSON_NAME
,T10.COMMISSION
,T10.COMMISSION_DATE
FROM (SELECT ROW_NUMBER() OVER(PARTITION BY T5.CUSTOMER_ID ORDER BY T5.LEAD_CREATED_DATE ASC) AS RN
,T5.*
,T6.MAX_COMMISSION_DATE
,DATEDIFF(DAY, T6.MAX_COMMISSION_DATE, T5.LEAD_CREATED_DATE) AS ANTAL_DAGE_SIDEN_SIDSTE_COMMISSION
,CASE
WHEN DATEDIFF(DAY, T6.MAX_COMMISSION_DATE, T5.LEAD_CREATED_DATE) > 365 THEN 'YES'
ELSE 'NO'
END AS COMMISSION
,CASE
WHEN DATEDIFF(DAY, T6.MAX_COMMISSION_DATE, T5.LEAD_CREATED_DATE) > 365 THEN T5.LEAD_CREATED_DATE
ELSE NULL
END AS COMMISSION_DATE
FROM #LEADS AS T5
INNER JOIN (SELECT T4.CUSTOMER_ID
,MAX(T4.COMMISSION_DATE) AS MAX_COMMISSION_DATE
FROM COMMISSION_CALCULATION AS T4
GROUP BY T4.CUSTOMER_ID) AS T6 ON T5.CUSTOMER_ID = T6.CUSTOMER_ID
WHERE T5.LEAD_ID NOT IN (SELECT LEAD_ID FROM COMMISSION_CALCULATION)
) AS T10
WHERE RN = 1
)
SELECT *
FROM COMMISSION_CALCULATION;
I have made some assumptions where your description does not fully make sense as written, but the below achieves your desired result:
if object_id('tempdb.dbo.#leads', 'u') is not null
drop table #leads;
create table #leads (lead_id int, customer_id int, lead_created_date datetime, salesperson_name varchar(20))
insert into #leads
values (1, 1, '2013-09-01', 'rasmus')
,(2, 1, '2013-11-01', 'christian')
,(3, 1, '2014-01-01', 'nadja')
,(4, 1, '2014-12-24', 'roar')
,(5, 1, '2015-12-01', 'kristian')
,(6, 2, '2014-01-05', 'knud')
,(7, 2, '2015-01-02', 'rasmus')
,(8, 2, '2015-01-08', 'roar')
,(9, 2, '2016-02-05', 'kristian')
,(10, 2, '2016-03-05', 'casper')
if object_id('tempdb.dbo.#disered_result', 'u') is not null
drop table #disered_result;
create table #disered_result (lead_id int, desired_commision_result char(3))
insert into #disered_result
values (1, 'yes'),(2, 'no'),(3, 'no'),(4, 'yes'),(5, 'no'),(6, 'yes'),(7, 'no'),(8, 'yes'),(9, 'yes'),(10, 'no')
with rownum
as
(
select row_number() over (order by customer_id, lead_created_date) as rn -- This is to ensure an incremantal ordering id
,lead_id
,customer_id
,lead_created_date
,salesperson_name
from #leads
)
,cte
as
(
select rn
,lead_id
,customer_id
,lead_created_date
,salesperson_name
,'yes' as commission_result
,lead_created_date as commission_window_start
from rownum
where rn = 1
union all
select r.rn
,r.lead_id
,r.customer_id
,r.lead_created_date
,r.salesperson_name
,case when r.customer_id <> c.customer_id -- If the customer ID has changed, we are at a new commission window.
then 'yes'
else case when r.lead_created_date > dateadd(d,365,c.commission_window_start) -- This assumes the window is 365 days and not one year (ie. Leap years don't add a day)
then 'yes'
else 'no'
end
end as commission_result
,case when r.customer_id <> c.customer_id
then r.lead_created_date
else case when r.lead_created_date > dateadd(d,365,c.commission_window_start) -- This assumes the window is 365 days and not one year (ie. Leap years don't add a day)
then r.lead_created_date
else c.commission_window_start
end
end as commission_window_start
from rownum r
inner join cte c
on(r.rn = c.rn+1)
)
select lead_id
,commission_result
from cte
order by customer_id
,lead_created_date;