Related
In SQL Server 2017 I have a table that looks like this https://i.stack.imgur.com/Ry106.png and I would like to get the amount of members at the end of each month, filling out the blank months with the data from the previous month.
So having this table
Create table #tempCenters (
OperationId int identity (1,1) primary key,
CenterId int,
members int,
Change_date date,
Address varchar(100), --non relevant
Sales float --non relevant
)
with this data
INSERT INTO #tempCenters VALUES
(1, 100, '2020-02-20', 'non relevant column', 135135),
(1, 110, '2020-04-15', 'non relevant column', 231635),
(1, 130, '2020-04-25', 'non relevant column', 3565432),
(1, 180, '2020-09-01', 'non relevant column', 231651),
(2, 200, '2020-01-20', 'non relevant column', 321365),
(2, 106, '2020-03-20', 'non relevant column', 34534),
(2, 135, '2020-06-25', 'non relevant column', 3224),
(2, 154, '2020-06-20', 'non relevant column', 2453453)
I am expecting this result
CenterId, Members, EOM_Date
1, 100, '2020-2-28'
1, 100, '2020-3-30'
1, 130, '2020-4-31'
1, 130, '2020-5-30'
1, 130, '2020-6-31'
1, 130, '2020-7-31'
1, 130, '2020-8-30'
1, 180, '2020-9-31'
2, 200, '2020-1-31'
2, 200, '2020-2-28'
2, 106, '2020-3-31'
2, 106, '2020-4-30'
2, 106, '2020-5-31'
2, 135, '2020-6-30'
And this is what I´ve got so far
SELECT
t.centerId,
EOMONTH(t.Change_date) as endOfMonthDate,
t.members
FROM #tempCenters t
RIGHT JOIN (
SELECT
S.CenterId,
Year(S.Change_date) as dateYear,
Month(S.Change_date) as dateMonth,
Max(s.OperationId) as id
FROM #tempCenters S
GROUP BY CenterId, Year(Change_date), Month(Change_date)
) A
ON A.id = t.OperationId
which returns the values per month, but not fill the blank ones.
First I get start date (min date) and finish date (max date) for each CenterId. Then I generate all end of months from start date to finish date for each CenterId. Finally I join my subuqery (cte) with your table (on cte.CenterId = tc.CenterId AND cte.EOM_Date >= tc.Change_date) and get last (previous or same date) members value for each date (end of month).
WITH cte AS (SELECT CenterId, EOMONTH(MIN(Change_date)) AS EOM_Date, EOMONTH(MAX(Change_date)) AS finish
FROM #tempCenters
GROUP BY CenterId
UNION ALL
SELECT CenterId, EOMONTH(DATEADD(MONTH, 1, EOM_Date)), finish
FROM cte
WHERE EOM_Date < finish)
SELECT DISTINCT cte.CenterId,
FIRST_VALUE(Members) OVER(PARTITION BY cte.CenterId, cte.EOM_Date ORDER BY tc.Change_date DESC) AS Members,
cte.EOM_Date
FROM cte
LEFT JOIN #tempCenters tc ON cte.CenterId = tc.CenterId AND cte.EOM_Date >= tc.Change_date
ORDER BY CenterId, EOM_Date;
I know it looks cumbersome and I'm sure there is a more elegant solution, but still you can use a combination of subqueries with union all and outer apply to get the desired result.
Select t.CenterId, Coalesce(t.members, tt.members), t.Change_date
From (
Select CenterId, Max(members) As members, Change_date
From
(Select t.CenterId, t.members, EOMONTH(t.Change_date) As Change_date
From #tempCenters As t Inner Join
(Select CenterId, Max(Change_date) As Change_date
From #tempCenters
Group by CenterId, Year(Change_date), Month(Change_date)
) As tt On (t.CenterId=tt.CenterId And
t.Change_date=tt.Change_date)
Union All
Select t.CenterId, Null As member, t.Change_date
From (
Select tt.CenterId, EOMONTH(datefromparts(tt.[YEAR], t.[MONTH], '1')) As Change_date,
Min_Change_date, Max_Change_date
From (Select [value] as [Month] From OPENJSON('[1,2,3,4,5,6,7,8,9,10,11,12]')) As t,
(Select CenterId, Year(Change_date) As [YEAR],
Min(Change_date) As Min_Change_date, Max(Change_date) As Max_Change_date
From #tempCenters Group by CenterId, Year(Change_date)) As tt) As t
Where Change_date Between Min_Change_date And Max_Change_date) As t
Group by CenterId, Change_date) As t Outer Apply
(Select members
From #tempCenters
Where CenterId=t.CenterId And
Change_date = (Select Max(Change_date)
From #tempCenters Where CenterId=t.CenterId And Change_date<t.Change_date Group by CenterId)) As tt
Order by t.CenterId, t.Change_date
Would you, pleace, help me, to count cummulative sum in sql server 2017. Condition is: 1) partition by client 2) order by date_tm. Desirable result is in the table below.
create table #clients (client nvarchar(1)
, date_tm datetime
,sum_pay int
, desirable_result int)
insert into #clients
(client, date_tm, sum_pay, desirable_result)
select '1', '2020-01-01', 10, 10 union all
select '1', '2020-01-02', 20, 30 union all
select '2', '2020-01-03', 20, 60 union all
select '2', '2020-01-01', 20, 20 union all
select '2', '2020-01-02', 20, 40 union all
select '3', '2020-01-01', 20, 20 union all
select '3', '2020-01-04', 20, 70 union all
select '3', '2020-01-02', 30, 50
select * from #clients
drop table if exists #clients
Thank you very much.
are finding below
select c.*,sum(sum_pay) over(partition by client order by date_tm)
from #clients c
You can use sum()over() window function as below:
select * ,SUM (sum_pay) OVER (partition by client order by date_tm) AS cummulativesum from #clients
SELECT * ,
CASE WHEN desirable_result = cum_sum THEN 'OK' ELSE 'NO' END AS Status
FROM
(
select
*,
SUM (sum_pay) OVER (partition by client order by date_tm) AS cum_sum
from #clients as tbl
) as a
with this code you can compare, desirable_result and cummilative sum
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)
);
I'm trying to find max number of continues days for each Cust_Nbr. For each year he can have multiple ones with or without breaks,in example below Cust has 3 PolicNo and for this case I need to find that max days = 5 (like for PolicNo = 1) and max=2 for other. I did try to work with some numbers, finished with the table like below but not sure how I can go thru all this for each by Cust_Nbr to catch any break. Appreciate your leads. I also pasted test data. Still have errors in my code .
Drop table if exists #T;
CREATE TABLE #t (Cust_Nbr int, PolicNo int, ENR_START Date, ENR_END DATE, DD Date, DayNum int, DayNum_Prev Int)
insert #t values
(11111, 1, '11/27/2019', '12/1/2019', '11/27/2019', 331, 0),
(11111, 1, '11/27/2019', '12/1/2019', '11/28/2019', 332, 331),
(11111, 1, '11/27/2019', '12/1/2019', '11/29/2019', 333, 332),
(11111, 1, '11/27/2019', '12/1/2019', '11/30/2019', 334, 333),
(11111, 1, '11/27/2019', '12/1/2019', '12/1/2019', 335, 334),
(11111, 22,'12/8/2019', '12/10/2019', '12/8/2019', 342, 335),
(11111, 22, '12/8/2019', '12/10/2019', '12/9/2019', 343, 342),
(11111, 22, '12/8/2019', '12/10/2019', '12/10/2019', 344, 343),
(11111, 333, '12/26/2019', '12/29/2019', '12/26/2019', 360, 344),
(11111, 333, '12/26/2019', '12/29/2019', '12/27/2019', 361, 360),
(11111, 333, '12/26/2019', '12/29/2019', '12/28/2019', 362, 361),
(11111, 333, '12/26/2019', '12/29/2019', '12/29/2019', 363, 362),
(999999, 90, '12/8/2019', '12/9/2019', '12/8/2019', 342, 0),
(999999, 90, '12/8/2019', '12/9/2019', '12/9/2019', 343, 342)
Select t.* , Days_Max = ( SELECT TOP 1 Cust_Nbr, COUNT(*)
FROM #t t2
WHERE t2.Cust_Nbr = t.Cust_Nbr
)
FROM #t t
Looking at the example you can actually do this with below query.
;WITH CTE1 AS
(
SELECT Cust_Nbr,
PolicNo,
COUNT(DayNum_Prev) as max_days,
ROW_NUMBER() OVER (PARTITION BY Cust_Nbr ORDER BY COUNT(DayNum_Prev) DESC) RN
FROM #t
GROUP BY Cust_Nbr,PolicNo
)
SELECT Cust_Nbr ,PolicNo,max_days FROM CTE1 WHERE RN=1
Or using your logic as below
;WITH CTE2 AS
(
SELECT *,ROW_NUMBER() OVER (ORDER BY Cust_Nbr,PolicNo) Rnk ,
CASE WHEN DayNum_Prev=DayNum-1 THEN 0 ELSE 1 END cols
FROM #t
)
,CTE3 AS
(
SELECT *,SUM(cols) OVER(ORDER BY Rnk) Grouper FROM CTE2
)
,CTE4 AS
(
SELECT Cust_Nbr,PolicNo,count(*) MAX_DAYS,
ROW_NUMBER() OVER (PARTITION BY Cust_Nbr ORDER BY COUNT(*) DESC) RN
FROM CTE3
GROUP BY Cust_Nbr,PolicNo,grouper
)
SELECT CUST_NBR,POLICNO,MAX_DAYS FROM CTE4 WHERE RN =1
Check Demo Here
Below solution is in PostgreSQL but you may apply similar logic in tsql. here is the demo.
select
cust_nbr,
policNo,
total
from
(
select
cust_nbr,
policNo,
count(rnk) as total,
dense_rank() over (partition by cust_nbr order by count(rnk) desc) as nrnk
from
(
select
cust_nbr,
policNo,
(dd - '2000-01-01'::date
- row_number() over (partition by cust_nbr, policNo order by dd)) as rnk
from myTable
) val
group by
cust_nbr,
policNo,
rnk
) res
where nrnk = 1
Output:
*-----------------------*
|cust_nbr policno total |
*-----------------------*
|11111 1 5 |
|999999 99 2 |
*-----------------------*
Hey guys i am trying to run this query in my postgres db but it returns an error: [Err] ERROR: syntax error at or near ","
LINE 13: and not substr(a.zoneiddest , 1 ,3) = any ('254','255','256'...
The query is like this
SELECT
to_char(a.CALLDATE, 'yyyymm') AS month,
min(a.calldate) AS start_time,
max(a.calldate) AS end_time,
ceil(SUM(a.CALLDURATION::INT) / 60) AS minutes,
COUNT(DISTINCT a.IDENTIFIANT) AS distinct_callers,
a.zoneiddest AS country_code,
b.country
FROM cdr_data a,
country_codes b
WHERE a.CALLSUBCLASS = '002'
AND a.CALLCLASS = '008'
AND a.zoneiddest::INT > 0
AND SUBSTR(a.CALLEDNUMBER, 1, 2) NOT IN
( '77', '78', '75', '70', '71', '41', '31', '39', '76', '79' )
AND NOT substr(a.zoneiddest, 1, 3) = ANY
( '254', '255','256', '211', '257', '250', '256' )
AND trim(a.zoneiddest) = trim(b.country_code)
GROUP BY
to_char(a.CALLDATE, 'yyyymm'),
a.zoneiddest,
b.country
ORDER BY 1
This same query works well in oracle with just a small minor change on a.zoneiddest::integer > 0 to just a.zoneiddest > 0
What could i be doing wrong
The problem is with your ANY operator. If i understand your query correctly, you can just substitute it with a NOT IN statement.
SELECT to_char (a.CALLDATE,'yyyymm') as month,min(a.calldate) as
start_time,max(a.calldate) as end_time,
ceil(SUM (a.CALLDURATION::integer) / 60) AS minutes,
COUNT (DISTINCT a.IDENTIFIANT) AS distinct_callers,
a.zoneiddest as country_code,b.country
FROM cdr_data a,COUNTRY_CODES b
WHERE a.CALLSUBCLASS = '002'
AND a.CALLCLASS = '008'
and a.zoneiddest::integer > 0
AND SUBSTR (a.CALLEDNUMBER, 1, 2) NOT IN
('77', '78', '75', '70', '71', '41', '31', '39', '76','79')
// This line
AND substr(a.zoneiddest , 1 ,3) NOT IN
('254','255','256','211','257','250','256')
// End of line
and trim(a.zoneiddest) = trim(b.country_code)
GROUP BY to_char (a.CALLDATE,'yyyymm') ,a.zoneiddest,b.country
ORDER BY 1
Try using keyword any combine with parameter array like this:
= any (ARRAY['254','255','256','211','257','250','256'])