Increment row depending on value of another column - sql

I have a sql query below, where dtMontno could start from any month and am adding Row column manually as below :
SELECT COUNT(*) as count,
MONTH(TourTbl.DT_Started) as dtMonthno,
DATENAME(YYYY, TourTbl.DT_Started) as dtYear,
row_number() over (order by DATENAME(YYYY, TourTbl.DT_Started) asc,
MONTH(TourTbl.DT_Started) asc ) as Row
FROM TourTbl
INNER JOIN BranchTbl ON TourTbl.BranchID = BranchTbl.BranchID
INNER JOIN AgencyTbl on AgencyTbl.AgencyID = BranchTbl.AgencyID
WHERE Cancelled = 0 AND
(TourTbl.DT_Started >= '2010/03/15' and
TourTbl.DT_Started <= '2012/03/15') AND
AgencyTbl.AgencyID in ( 245 ) and
BranchRODID > 0
group by datename(M, TourTbl.DT_Started),
DATENAME(YYYY, TourTbl.DT_Started),
MONTH(TourTbl.DT_Started)
order by dtYear asc, dtMonthno asc
now my result is :
count dtMonthno dtYear Row
6 5 2011 1
8 6 2011 2
2 7 2011 3
23 8 2011 4
126 9 2011 5
101 10 2011 6
85 11 2011 7
92 12 2011 8
115 1 2012 9
102 2 2012 10
48 3 2012 11
Is there any way to start the Row column depending on the dtMonthno and increment by one in the example above would start from 5 and end in 15?
Thanks

Try changing the derivation of Row to:
row_number() over (order by YEAR(TourTbl.DT_Started) asc,
MONTH(TourTbl.DT_Started) asc ) +
min(YEAR(TourTbl.DT_Started)*12+MONTH(TourTbl.DT_Started)-1) OVER () % 12 as Row

You can add month of first DT_Started date:
SELECT COUNT(*) as count,
MONTH(TourTbl.DT_Started) as dtMonthno,
DATENAME(YYYY, TourTbl.DT_Started) as dtYear,
row_number() over (order by DATENAME(YYYY, TourTbl.DT_Started) asc,
MONTH(TourTbl.DT_Started) asc )
+ substring(min(DATENAME(YYYY, [TourTbl].DT_Started) + right ('0' + str (MONTH([TourTbl].DT_Started), 2), 2)) over (), 5, 2) - 1 as Row
FROM TourTbl
INNER JOIN BranchTbl ON TourTbl.BranchID = BranchTbl.BranchID
INNER JOIN AgencyTbl on AgencyTbl.AgencyID = BranchTbl.AgencyID
WHERE Cancelled = 0 AND
(TourTbl.DT_Started >= '2010/03/15' and
TourTbl.DT_Started <= '2012/03/15') AND
AgencyTbl.AgencyID in ( 245 ) and
BranchRODID > 0
group by datename(M, TourTbl.DT_Started),
DATENAME(YYYY, TourTbl.DT_Started),
MONTH(TourTbl.DT_Started)
order by dtYear asc, dtMonthno asc

I would truncate the dates to months and group by those values, then obtain years, months and row numbers based on the truncated dates:
SELECT
COUNT(*) AS count,
MONTH(GroupMonth) AS dtMonthno,
DATENAME(YYYY, GroupMonth) AS dtYear, /* why do you want year as a string? */
ROW_NUMBER() OVER (ORDER BY GroupMonth) + MONTH(MIN(GroupMonth) OVER ()) - 1 AS Row
FROM (
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, TourTbl.DT_Started), 0) AS GroupMonth
FROM TourTbl
INNER JOIN BranchTbl ON TourTbl.BranchID = BranchTbl.BranchID
INNER JOIN AgencyTbl on AgencyTbl.AgencyID = BranchTbl.AgencyID
WHERE Cancelled = 0 AND
(TourTbl.DT_Started >= '2010/03/15' and
TourTbl.DT_Started <= '2012/03/15') AND
AgencyTbl.AgencyID in ( 245 ) and
BranchRODID > 0
) s
GROUP BY GroupMonth

Related

Window function (lead, lag) to sumup if the partition name is the same

I need to get the previous and next row based on the partition by groupid, buid and packageid.
The issue is that a customer may have two packages within the same partition. What would be the best way to handle this? I cannot group because I will need that extra row later. Thanks
The end goal is to calculate the difference between the previous month and the current month as well as previous month and next month. This is needed to calculate the churn, reactivation and so on.
I need to either remove the previous and next columns and only look at the sum of the values based on the partition that are invoices date, groupid, buid and item_id.
The query that I wrote below, should be based on the sum of these values, I highlighted in red.
date
groupid
buid
item_id
previous
next
amount
1/1
1
2
5
20
20
1/2
1
2
6
10
10
1/1
1
2
6
10
10
2/1
1
2
5
20
20
20
2/1
1
2
6
10
10
10
2/1
1
2
6
10
10
3/1
1
2
5
20
20
3/1
1
2
6
10
20
This is my query so far:
SELECT
*
, "lag"(amount_usd, 1) OVER (PARTITION BY invoice_item_customer_id, invoice_item_related_customer_id, item_id ORDER BY "date_trunc"('month', date_invoice), package_name ASC) prev_amount
, "lead"(amount_usd, 1) OVER (PARTITION BY invoice_item_customer_id, invoice_item_related_customer_id, item_id ORDER BY "date_trunc"('month', date_invoice), package_name ASC) next_amount
, "lag"(date_invoice, 1) OVER (PARTITION BY invoice_item_customer_id, invoice_item_related_customer_id, item_id ORDER BY "date_trunc"('month', date_invoice), package_name ASC) prev_invoice_date
, "lead"(date_invoice, 1) OVER (PARTITION BY invoice_item_customer_id, invoice_item_related_customer_id, item_id ORDER BY "date_trunc"('month', date_invoice), package_name ASC) next_invoice_date
FROM
group_cust
)
, cte_status AS (
SELECT
*
, (CASE WHEN (((amount_usd >= 0) AND (next_amount IS NULL)) AND ((date_invoice < "date_add"('month', -1, current_date)) OR ("date_trunc"('month', next_invoice_date) > "date_add"('month', 1, "date_trunc"('month', date_invoice))))) THEN 'churn_next_month' ELSE null END) churn
, (CASE WHEN ((CAST("date_trunc"('month', first_invoice_date_package) AS date) = "date_trunc"('month', date_invoice)) AND (amount_usd >= 0)) THEN 'new' WHEN (((CAST("date_trunc"('month', first_invoice_date) AS date) < "date_trunc"('month', date_invoice)) AND (amount_usd >= 0)) AND ("date_trunc"('month', prev_invoice_date) < "date_add"('month', -1, "date_trunc"('month', date_invoice)))) THEN 'reactivation' WHEN ((first_invoice_date_buid > first_invoice_date) AND ("date_trunc"('month', date_invoice) = first_invoice_date_buid)) THEN 'group expansion' WHEN (((amount_usd >= 0) AND (prev_amount >= 0)) AND (amount_usd > prev_amount)) THEN 'upsell' WHEN (((amount_usd >= 0) AND (prev_amount >= 0)) AND (amount_usd < prev_amount)) THEN 'downsell' WHEN (((amount_usd >= 0) AND (prev_amount IS NOT NULL)) AND (amount_usd = prev_amount)) THEN 'same' ELSE null END) donor_status
FROM
cte_period
)

How to join a table on two columns by selecting max value? SQL Server

I am working on a View and have the following table which I try to modify. (SQL Server)
Table X
ID Value End_time
1 10 2019-12-06T07:00:00+0000
2 15 2020-01-07T07:00:00+0000
2 20 2020-01-31T07:00:00+0000
3 25 2020-02-02T07:00:00+0000
3 30 2020-02-28T07:00:00+0000
4 35 2020-03-05T07:00:00+0000
4 40 2020-03-31T07:00:00+0000
The [End_time] has to be converted from varchar into date and further splitter in month and year, then I have to select the max date and group by the data by year and month.
So I make the following query
SELECT [ID], [Value], CONVERT(date, LEFT([End_time], LEN([End_time]) - 14)) AS [date],
-- convert to date
MONTH(CONVERT(date, LEFT([End_time], LEN([End_time]) - 14))) AS Month_Name, --get month
YEAR(CONVERT(date, LEFT([End_time], LEN([End_time]) - 14))) AS Year_Name --get year
FROM X AS Table
INNER JOIN --join on itself
(SELECT MAX([date]) AS MaxDate, MONTH([date]) AS Month_Name, YEAR(date) AS Year_Name
FROM X
GROUP BY MONTH([date]), YEAR([date])) AS D ON D.MaxDate = CONVERT(date,
LEFT([End_time], LEN([End_time]) - 14))
But I get an error that there's no such column as date.
I tried to join like this:
INNER JOIN --join on itself
(SELECT MAX(CONVERT(date, LEFT([End_time], LEN([End_time]) - 14))) AS MaxDate,
MONTH([date]) AS Month_Name, YEAR(date) AS Year_Name
FROM X
GROUP BY MONTH([date]), YEAR([date])) AS D ON D.MaxDate = CONVERT(date,
LEFT([End_time], LEN([End_time]) - 14))
But then I get the error that The multi-part identifier "X.End_time" could not be bound.
So my ideal result is
ID Value Month_Name Year_Name
1 10 12 2019
2 35 01 2020
3 55 02 2020
4 75 03 2020
I would just use:
select id, value,
substring(endtime, 5, 2) as month,
left(endtime, 4) as year
from (select t.*,
row_number() over (partition by id order by endtime desc) as seqnum
from x
) x
where seqnum = 1;
Although storing a date/time value as a string is a bad choice of representation, you have at least chosen a very good format for the string. You can just order by the string to get the most recent or oldest row.
SELECT MAX([table_alias.ID]),
SUM([table_alias.VALUE]),
[table_alias.Month_Name],
[table_alias.Year_Name]
FROM ( SELECT [ID], [Value],
MONTH(CONVERT(date, LEFT([End_time], LEN([End_time]) - 14))) AS Month_Name,
YEAR(CONVERT(date, LEFT([End_time], LEN([End_time]) - 14))) AS Year_Name
FROM X) AS table_alias
group by [table_alias.Month_Name],
[table_alias.Year_Name]

SQL: add missing months from different years

SQL SERVER
[CreatedOn] - DATETIME
I get this table:
Year Month Count
2009 7 1
2009 9 1
2010 1 2
2010 3 13
From query:
SELECT
YEAR ([CreatedOn]) AS 'Year',
MONTH ([CreatedOn]) AS 'Month',
COUNT ([CreatedOn]) AS 'Count'
FROM xxx
GROUP BY YEAR ([CreatedOn]), MONTH ([CreatedOn])
How can I get table like this (with missed months and Count 0):
Year Month Count
2009 7 1
2009 8 0
2009 9 1
2009 10 0
2009 11 0
2009 12 0
2010 1 2
2010 2 0
2010 3 13
Syntax says you are using MSSQL. Use Recursive CTE to generate the calender table then do a Left outer join with XXX table
DECLARE #maxdate DATE = (SELECT Max([CreatedOn])
FROM xxx);
WITH calender
AS (SELECT Min([CreatedOn]) dates,
FROM xxx
UNION ALL
SELECT Dateadd(mm, 1, dates)
FROM cte
WHERE dates < #maxdate)
SELECT Year(dates) [YEAR],
Month(dates) [month],
Count ([CreatedOn]) AS 'Count'
FROM calender a
LEFT OUTER JOIN xxx b
ON Year(dates) = Year ([CreatedOn])
AND Month(dates) = Month ([CreatedOn])
GROUP BY Year(dates),
Month(dates)
Note : Instead of Recursive CTE create a physical calender table
This will use a build in table to create the calendar:
;WITH limits as
(
SELECT min([CreatedOn]) mi, max([CreatedOn]) ma
FROM xxx
), months as(
SELECT
dateadd(mm, number, mi) m
FROM
master..spt_values v
JOIN
limits l
ON
number between 0 and datediff(mm, l.mi, l.ma)
WHERE
v.type = 'P'
)
SELECT
year(months.m) year,
month(months.m) month,
count(qry.[CreatedOn]) cnt
FROM
xxx qry
RIGHT JOIN
months
ON
months.m = dateadd(mm, datediff(mm, 0, qry.[CreatedOn]), 0)
GROUP BY
year(months.m),
month(months.m)

SUM from Specific Date until the end of the month SQL

I have the following table:
ID GROUPID oDate oValue
1 A 2014-06-01 100
2 A 2014-06-02 200
3 A 2014-06-03 300
4 A 2014-06-04 400
5 A 2014-06-05 500
FF. until the end of the month
30 A 2014-06-30 600
I have 3 kinds of GROUPID, and each group will create one record per day.
I want to calculate the total of oValue from the 2nd day of each month until the end of the month. So the total of June would be from 2/Jun/2014 until 30/Jun/2014. If July, then the total would be from 2/Jul/2014 until 31/Jul/2014.
The output will be like this (sample):
GROUPID MONTH YEAR tot_oValue
A 6 2014 2000
A 7 2014 3000
B 6 2014 1500
B 7 2014 5000
Does anyone know how to solve this with sql syntax?
Thank you.
You can use a correlated subquery to get this:
SELECT T.ID,
T.GroupID,
t.oDate,
T.oValue,
ct.TotalToEndOfMonth
FROM T
OUTER APPLY
( SELECT TotalToEndOfMonth = SUM(oValue)
FROM T AS T2
WHERE T2.GroupID = T.GroupID
AND T2.oDate >= T.oDate
AND T2.oDate < DATEADD(MONTH, DATEDIFF(MONTH, 0, T.oDate) + 1, 0)
) AS ct;
For your example data this gives:
ID GROUPID ODATE OVALUE TOTALTOENDOFMONTH
1 A 2014-06-01 100 2100
2 A 2014-06-02 200 2000
3 A 2014-06-03 300 1800
4 A 2014-06-04 400 1500
5 A 2014-06-05 500 1100
30 A 2014-06-30 600 600
Example on SQL Fiddle
For future reference if you ever upgrade, in SQL Server 2012 (and later) this becomes even easier with windowed aggregate functions that allow ordering:
SELECT T.*,
TotalToEndOfMonth = SUM(oValue)
OVER (PARTITION BY GroupID,
DATEPART(YEAR, oDate),
DATEPART(MONTH, oDate)
ORDER BY oDate DESC)
FROM T
ORDER BY oDate;
Example on SQL Fiddle
EDIT
If you only want this for the 2nd of each month, but still need all the fields then you can just filter the results of the first query I posted:
SELECT T.ID,
T.GroupID,
t.oDate,
T.oValue,
ct.TotalToEndOfMonth
FROM T
OUTER APPLY
( SELECT TotalToEndOfMonth = SUM(oValue)
FROM T AS T2
WHERE T2.GroupID = T.GroupID
AND T2.oDate >= T.oDate
AND T2.oDate < DATEADD(MONTH, DATEDIFF(MONTH, 0, T.oDate) + 1, 0)
) AS ct
WHERE DATEPART(DAY, T.oDate) = 2;
Example on SQL Fiddle
If you are only concerned with the total then you can use:
SELECT T.GroupID,
[Month] = DATEPART(MONTH, oDate),
[Year] = DATEPART(YEAR, oDate),
tot_oValue = SUM(T.oValue)
FROM T
WHERE DATEPART(DAY, T.oDate) >= 2
GROUP BY T.GroupID, DATEPART(MONTH, oDate), DATEPART(YEAR, oDate);
Example on SQL Fiddle
Not sure whether you have data for different years
Select YEAR(oDate),MONTH(oDate),SUM(Value)
From #Temp
Where DAY(oDate)>1
Group By YEAR(oDate),MONTH(oDate)
If you want grouped per GROUPID, year and month this should do it:
SELECT
GROUPID,
[MONTH] = MONTH(oDate),
[YEAR] = YEAR(oDate),
tot_oValue = SUM(ovalue)
FROM your_table
WHERE DAY(odate) > 1
GROUP BY GROUPID, YEAR(oDate), MONTH(oDate)
ORDER BY GROUPID, YEAR(oDate), MONTH(oDate)
This query produces required output:
SELECT GROUPID, MONTH(oDate) AS "Month", YEAR(oDate) AS "Year", SUM(oValue) AS tot_oValue
FROM table_name
WHERE DAY(oDate) > 1
GROUP BY GROUPID, YEAR(oDate), MONTH(oDate)
ORDER BY GROUPID, YEAR(oDate), MONTH(oDate)

How to arrange date/time into AM in/out and PM in/out

I am making a payroll system and I bought the B3 tft from zktechnology and would like to arrange the record.
Currently I can pull the data from the biometric with this format:
Count EmpID InOutMode Date
1 1 0 8/20/2012 07:49:01
2 1 1 8/20/2012 12:08:21
3 1 0 8/20/2012 12:43:10
4 1 1 8/20/2012 17:56:15
5 2 0 8/20/2012 07:53:11
6 2 1 8/20/2012 12:02:01
7 2 0 8/20/2012 12:39:56
8 2 1 8/20/2012 17:20:43
9 1 0 8/21/2012 08:10:20
10 1 1 8/21/2012 12:01:26
11 1 0 8/21/2012 13:03:11
12 1 1 8/21/2012 17:11:15
13 2 0 8/21/2012 07:48:26
14 2 1 8/21/2012 12:14:58
15 2 0 8/21/2012 12:59:31
16 2 1 8/21/2012 17:20:12
InOutMode:
0 = In, 1 = Out
Now, I want to convert the data above like this:
EmpID Date AM_In AM_Out PM_In PM_Out
1 8/20/2012 07:49:01 12:08:21 12:43:10 17:56:15
2 8/20/2012 07:53:11 12:02:01 12:39:56 17:20:43
1 8/21/2012 08:10:20 12:01:26 13:03:11 17:11:15
2 8/21/2012 07:48:26 12:14:58 12:59:31 17:20:12
So I can save it to the database with EmpID, Date, AM_In, AM_Out, PM_In, PM_Out fields.
I saw a similar code here before but I can't remember the URL.
Update:
VB.net code or sql in ms access format is acceptable.
It might be easiest to create two cross tabs and a query to join them to the available dates and employees.
1 AM Crosstab
TRANSFORM Min(tm.Date) AS MinOfDate
SELECT Format([Date],"dd/mm/yyyy") AS dt, tm.EmpID
FROM tm
GROUP BY Format([Date],"dd/mm/yyyy"), tm.EmpID
PIVOT tm.InOutMode;
2 PM Crosstab
TRANSFORM Max(tm.Date) AS MinOfDate
SELECT Format([Date],"dd/mm/yyyy") AS dt, tm.EmpID
FROM tm
GROUP BY Format([Date],"dd/mm/yyyy"), tm.EmpID
PIVOT tm.InOutMode;
Where tm is the name of your table.
You can then join these up.
SELECT Alldates.dt,
Alldates.empid,
am.[0] AS [Am In],
am.[1] AS [Am Out],
pm.[0] AS [Pm In],
pm.[1] AS [Pm Out]
FROM ((SELECT DISTINCT Format([date], "dd/mm/yyyy") AS dt,
empid
FROM tm) AS Alldates
LEFT JOIN am
ON ( Alldates.empid = am.empid )
AND ( Alldates.dt = am.dt ))
LEFT JOIN pm
ON ( Alldates.empid = pm.empid )
AND ( Alldates.dt = pm.dt );
Here is a query that should produce the results that you want in MS Access:
select am_in.empid,
format(am_in.min_in_dt, "MM/DD/YYYY") as [date],
format(am_in.min_in_dt, "hh:mm:ss") as AM_In,
format(am_out.min_out_dt, "hh:mm:ss") as AM_Out,
format(pm_in.max_in_dt, "hh:mm:ss") as PM_In,
format(pm_out.max_out_dt, "hh:mm:ss") as PM_Out
from
(
(
(
SELECT empid,
min(dt) as min_in_dt
FROM yourTable
where inoutmode = 0
group by empid
) am_in
inner join
(
SELECT empid,
min(dt) as min_out_dt
FROM yourTable
where inoutmode = 1
group by empid
) am_out
on am_in.empid = am_out.empid
)
inner join
(
SELECT empid,
max(dt) as max_in_dt
FROM yourTable
where inoutmode = 0
group by empid
) pm_in
on am_in.empid = pm_in.empid
)
inner join
(
SELECT empid,
max(dt) as max_out_dt
FROM yourTable
where inoutmode = 1
group by empid
) pm_out
on am_in.empid = pm_out.empid
Something like this will work
select
empid,
dateadd(day,datediff(day,0,DATE),0) as date,
max(case when sno=1 then convert(varchar(8),DATE,108)) as AM_IN,
max(case when sno=2 then convert(varchar(8),DATE,108)) as AM_OUT,
max(case when sno=3 then convert(varchar(8),DATE,108)) as PM_IN,
max(case when sno=4 then convert(varchar(8),DATE,108)) as PM_OUT
from
(
select *,
row_number() over (partition by empid order by Empid) as sno
from
table
) as t
group by
empid,dateadd(day,datediff(day,0,DATE),0)