I have a SAP-configured TFACS calendar source table in this format:
This is the source table. This is a calendar table, and each "1 or 0" in the Month column represents a day, and 1 is for days worked and 0 is for days not worked for each year based off of the US holiday Calendar
I am looking to transform this table into this format:
For the entire table.
Does anybody know of any way to accomplish this in SQL Server environment?
The easiest way is to join with a calendar table.
Then use the day of the month to extract the 0/1 from the month strings.
select
cal_year as [Year]
, cal_month as [Month]
, cal_day as [Day]
, try_cast(case cal_month
when 1 then substring(Mon1, cal_day, 1)
when 2 then substring(Mon2, cal_day, 1)
when 3 then substring(Mon3, cal_day, 1)
when 4 then substring(Mon4, cal_day, 1)
when 5 then substring(Mon5, cal_day, 1)
when 6 then substring(Mon6, cal_day, 1)
when 7 then substring(Mon7, cal_day, 1)
when 8 then substring(Mon8, cal_day, 1)
when 9 then substring(Mon9, cal_day, 1)
when 10 then substring(Mon10, cal_day, 1)
when 11 then substring(Mon11, cal_day, 1)
when 12 then substring(Mon12, cal_day, 1)
end as tinyint) as [Working Day]
from your_tfacs_calendar t
join ref_calendar cal
on cal.cal_year = t.year;
The creation of the REF_CALENDAR table in this example query can be found in this old SO post.
I ended up with this, it solves the case.
with calendar as
( select cast('2005-01-01' as date) as [Date], 2022 as [Year], 1 as [Month], 1
as [Day]
union all
select dateadd(day,1,[Date]), DATEPART(year,dateadd(day,1,[Date])),
DATEPART(MONTH,dateadd(day,1,[Date])), DATEPART(DAY,dateadd(day,1,[Date]))
from calendar
where [Date] <= '2030-12-31'
)
select c.*,
CASE c.month when 1 then SUBSTRING(t.TFACS_MON01,c.Day,1)
when 2 then
SUBSTRING(t.TFACS_MON02,c.Day,1)
when 3 then
SUBSTRING(t.TFACS_MON03,c.Day,1)
when 4 then
SUBSTRING(t.TFACS_MON04,c.Day,1)
when 5 then
SUBSTRING(t.TFACS_MON05,c.Day,1)
when 6 then
SUBSTRING(t.TFACS_MON06,c.Day,1)
when 7 then
SUBSTRING(t.TFACS_MON07,c.Day,1)
when 8 then
SUBSTRING(t.TFACS_MON08,c.Day,1)
when 9 then
SUBSTRING(t.TFACS_MON09,c.Day,1)
when 10 then
SUBSTRING(t.TFACS_MON10,c.Day,1)
when 11 then
SUBSTRING(t.TFACS_MON11,c.Day,1)
when 12 then
SUBSTRING(t.TFACS_MON12,c.Day,1)
END as WorkDay
from calendar c
join [TFACS] t on t.TFACS_JAHR = c.Year
ORDER BY Date
OPTION(MAXRECURSION 30000)
Related
I am trying to calculate numbers of customers whom are active in the past 3 and 6 months.
SELECT COUNT (DISTINCT CustomerNo)
FROM SalesDetail
WHERE InvoiceDate > (GETDATE() - 180) AND InvoiceDate < (GETDATE() - 90)
SELECT COUNT (DISTINCT CustomerNo)
FROM SalesDetail
WHERE InvoiceDate > (GETDATE() - 90)
However, based on above query, I'll get count Customers which has been active for both in the last 3 months and the last 6 months, even if there are duplicates like this.
Customer A bought once in past 3 months
Customer A bought once in past 6 months too
How do I filter out the customers, so that if customer A has been active in both past 3 and 6 months, he/she will only be counted in the 'active in past 3 months' query and not in the 'active in past 6 months' too.
I solve this problem this way
Let us consider you have following table. You might have more columns but for the result you want, we only require customer_id and date they bought something on.
CREATE TABLE [dbo].[customer_invoice](
[id] [int] IDENTITY(1,1) NOT NULL,
[customer_id] [int] NULL,
[date] [date] NULL,
CONSTRAINT [PK_customer_invoice] PRIMARY KEY([id]);
I created this sample data on this table
INSERT INTO [dbo].[customer_invoice]
([customer_id]
,[date])
VALUES
(1,convert(date,'2019-12-01')),
(2,convert(date,'2019-11-05')),
(2,convert(date,'2019-8-01')),
(3,convert(date,'2019-7-01')),
(4,convert(date,'2019-4-01'));
Lets not try to jump directly on the final solution directly but take a single leap each time.
SELECT customer_id, MIN(DATEDIFF(DAY,date,GETDATE())) AS lastActiveDays
FROM customer_invoice GROUP BY customer_id;
The above query gives you the number of days before each customer was active
customer_id lastActiveDays
1 15
2 41
3 168
4 259
Now We will use this query as subquery and Add a new column ActiveWithinCategory so that in later step we can group our data by the column.
SELECT customer_id, lastActiveDays,
CASE WHEN lastActiveDays<90 THEN 'active within 3 months'
WHEN lastActiveDays<180 THEN 'active within 6 months'
ELSE 'not active' END AS ActiveWithinCategory
FROM(
SELECT customer_id, MIN(DATEDIFF(DAY,date,GETDATE())) AS lastActiveDays
FROM customer_invoice GROUP BY customer_id
)AS temptable;
This query gives you the the following result
customer_id lastActiveDays ActiveWithinCategory
1 15 active within 3 months
2 41 active within 3 months
3 168 active within 6 months
4 259 not active
Now use the above whole thing as subquery and Group the data using ActiveWithinCategory
SELECT ActiveWithinCategory, COUNT(*) AS NumberofCustomers FROM (
SELECT customer_id, lastActiveDays,
CASE WHEN lastActiveDays<90 THEN 'active within 3 months'
WHEN lastActiveDays<180 THEN 'active within 6 months'
ELSE 'not active' END AS ActiveWithinCategory
FROM(
SELECT customer_id, MIN(DATEDIFF(DAY,date,GETDATE())) AS lastActiveDays
FROM customer_invoice GROUP BY customer_id
)AS temptable
) AS FinalResult GROUP BY ActiveWithinCategory;
And Here is your final result
ActiveWithinCategory NumberofEmployee
active within 3 months 2
active within 6 months 1
not active 1
If you want to achieve same thing is MySQL Database
Here is the final Query
SELECT ActiveWithinCategory, count(*) NumberofCustomers FROM(
SELECT MIN(DATEDIFF(curdate(),date)) AS lastActiveBefore,
IF(MIN(DATEDIFF(curdate(),date))<90,
'active within 3 months',
IF(MIN(DATEDIFF(curdate(),date))<180,'active within 6 months','not active')
) ActiveWithinCategory
FROM customer_invoice GROUP BY customer_id
) AS FinalResult GROUP BY ActiveWithinCategory;
I suspect that you want to do conditional aggregation here:
SELECT
CustomerNo,
COUNT(CASE WHEN InvoiceDate > GETDATE() - 90 THEN 1 END) AS cnt_last_3,
COUNT(CASE WHEN InvoiceDate > GETDATE() - 180 AND InvoiceDate < GETDATE() - 90
THEN 1 END) AS cnt_first_3
FROM yourTable
GROUP BY
CustomerNo;
Here cnt_last_3 is the count over the immediate past 3 months, and cnt_first_3 is the count from the 3 month period starting 6 months ago and ending 3 months ago.
If you want the distinct count you may add distinct like this
Select
count( Case when dt between getdate()- 90 and getdate() then id else null end) cnt_3_months
,count(distinct Case when dt between getdate() - 180 and getdate() - 90 then id else null end) cnt_6_months
from a
I have a table that shows only the 'captured' data. For example in the below exhibit, the emp_no 17 has 2 records - for November and February (for a specified 6 month period, from July 2017). It does not have data for the other 4 months (within the 6-month date range, from previous 6 months to current date).
How can I populate these missing months (Sept, Oct, Dec) with default values for num_differences of 0 for the missing months? (for example, in this case, I want emp_no 17 to have the below (I can ignore 2018 data - only require data up to Dec 2017):
I have the script below:
declare #YMN date;
set #YMN = '20171201';
DECLARE #Emp TABLE (
[date] date,
[emp_no] int,
[num_differences] int
);
INSERT INTO #Emp VALUES
('2017-09-14', 17, 1), ('2017-12-01', 17, 1),('2017-12-18', 17, 1),('2017-12-21', 17, 1),
('2017-09-27', 17, 1), ('2017-12-04', 17, 1);
-------------------------------------------------------------------------------------------get missing dates---------------------------------------------------------------------------
;WITH cte_Emp_No AS (
SELECT DISTINCT [emp_no]
FROM #Emp
),
cte_dates AS (
SELECT [emp_no], DATEADD(month, -6, DATEADD(dd, -(DAY(dateadd(month, 1, #YMN)) - 1), dateadd(month, 1, #YMN))) AS [date]
FROM cte_Emp_No
UNION ALL
SELECT [emp_no], DATEADD(month, 1, [date]) AS [date]
FROM cte_dates
WHERE [date] < dateadd(month, 0, #YMN)
)
SELECT DISTINCT ISNULL(e.emp_no, c.emp_no) emp_no, ISNULL(e.date, c.date) date, ISNULL(e.num_differences, 0) num_differences
into ##new_table
FROM #Emp AS e
RIGHT JOIN cte_dates AS c ON YEAR(c.date) = YEAR(e.date) AND MONTH(c.date) = MONTH(e.date)
-----------------------------------------------------------------------------------------------MAIN CTE------------------------------------------------------------------------------
;with cte_RawScore as
(
select emp_no
, date YMN
,sum(case when datediff(month, convert(datetime, #YMN, 112), date) = 0 then num_differences else 0 end) as thismonth
,sum(case when datediff(month, convert(datetime, #YMN, 112), date) between -2 and 0 then num_differences else 0 end) as last3month
,sum(case when datediff(month, convert(datetime, #YMN, 112), date) between -5 and 0 then num_differences else 0 end) as last6month
from ##new_table d
group by emp_no, date
)
select
emp_no
,YMN
,case when last6month = 0 then 5
when last3month = 0 then 4
when thismonth = 0 then 3
when thismonth <= 3 then 2
else 1 end RawScore
from cte_RawScore
ORDER BY day(YMN) desc
drop table ##new_table
I want this the scoring only to be applicable for 6 months from and after July 2017. i.e. the #YMN is a variable that stores the year month number; and the score, according to the above rule applies to the 6 months from 201707.
So 201707 is 1 month,
201708 is 2 months, etc, up to 201712
I wish to have a list of employees with their associated scores, based on the rules mentioned below .
That’s, :
A score of 5 if 0 differences in 6 consecutive months ( from July to December) ;
A score of 4 if 0 differences in 3 consecutive months (from July to December);
A score of 3 if 0 differences for 1 month ( from July to December);
A score of 2 if 1 to 3 differences for 1 month (from July to December);
A score of 1 if 4 or more differences in 1 month (from July to December).
I get the number of differences from a table, but some employees do not appear for certain months; hence I want to give them a difference of 0 if they do not appear for that particular month.
Please assist.
I think I understand what you're getting at. Let me give you a simplified example. You need a table full of dates to join to. In data warehousing we use a Date dimension which has attributes about every date.
For your example your date dimension table could just have Month names or numbers:
1
2
...
12
Let's call this table Months.
Then you would do something like this, to count a zero for months with no data. Here I'm using what's called a Common Table Expression or CTE (the part with the WITH) in place of a table, since I'm not concerned with creating a permanent table right now.
WITH Months AS (
SELECT 1 AS MonthNumber UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5 UNION
SELECT 6 UNION
SELECT 7 UNION
SELECT 8 UNION
SELECT 9 UNION
SELECT 10 UNION
SELECT 11 UNION
SELECT 12
)
SELECT M.MonthNumber, COUNT(*)
FROM Months as M
LEFT JOIN MyData as D
ON MONTH(D.SomeDateValue) = M.MonthNumber
GROUP BY M.MonthNumber
This will guarantee every month appears with a count, perhaps of zero.
I'm trying to get a query that returns customers I've attended per day, and i have this dataset:
fecha RecargadorPDV
2016/12/19 1
2016/12/19 2
2016/12/19 3
2016/12/20 1
2016/12/20 4
2016/12/20 5
2016/12/21 2
2016/12/21 6
2016/12/21 7
2016/12/21 8
..
...
2016/12/26 1
2016/12/26 2
2016/12/26 1
2016/12/27 2
2016/12/27 6
2016/12/27 7
2016/12/27 8
but the output I'd want to have is this:
date attended acum_customers
2016/12/19 3 3 -- Every monday it restart
2016/12/20 3 5
2016/12/21 4 8
.
..
2016/12/26 3 3 -- Every monday it restart
2016/12/27 4 3
.
..
As you can see, every monday it restart the values and if some customers are in a date and in the next day are present it needs to be ignored.
Here is a version that returns what you need for any arbitrary date. I have included sample data for a full week + two days to confirm its functionality.
DECLARE #t table (fecha date,
RecargadorPDV int
)
INSERT INTO #t VALUES
('2016/12/19', 1),
('2016/12/19', 2),
('2016/12/19', 3),
('2016/12/20', 1),
('2016/12/20', 4),
('2016/12/20', 5),
('2016/12/21', 2),
('2016/12/21', 6),
('2016/12/21', 7),
('2016/12/21', 8),
('20161222', 12),
('20161222', 1),
('20161222', 8),
('20161223', 11),
('20161223', 13),
('20161223', 15),
('20161223', 9),
('20161224', 1),
('20161225', 22),
('2016/12/26', 1),
('2016/12/26', 2),
('2016/12/26', 1),
('2016/12/27', 2),
('2016/12/27', 6),
('2016/12/27', 7),
('2016/12/27', 8)
;
With a as (
SELECT DISTINCT
fecha,
Dateadd(day, -(
case
when datepart(weekday, fecha) >=2
THEN datepart(weekday, fecha) - 2
ELSE 6
END), fecha) as LastMonday
FROM #t
)
SELECT
a.fecha as [date],
-- count(Distinct(CASE when t.fecha = a.fecha Then t.recargadorPDV else -1 END)) - 1 as attended,
SUM(CASE when t.fecha = a.fecha Then 1 else 0 END) as attended,
Count(distinct recargadorPDV) as acum_customers
FROM #t t
INNER JOIN a
ON t.fecha BETWEEN a.LastMonday and a.fecha
Group by a.fecha
ORDER BY a.fecha
Output of the above (as corrected) is:
date attended acum_customers
2016-12-19 3 3
2016-12-20 3 5
2016-12-21 4 8
2016-12-22 3 9
2016-12-23 4 13
2016-12-24 1 13
2016-12-25 1 14
2016-12-26 3 2
2016-12-27 4 5
I think your second week acum_customers is off based off the test data so check on that. I also assumed that the recargadorPDVcould only attend once per day since it's unique so I removed the one record noted below. With that... this should get you what you want. Let me know if it needs more explanation.
--change the ##DATEFIRST from 7 (english default) to 1 for the start of the week calculations
SET DATEFIRST 1;
--load some test data
declare #table table (fetcha datetime, recargadorPDV int)
insert into #table(fetcha, recargadorPDV)
values
('2016/12/19',1),
('2016/12/19',2),
('2016/12/19',3),
('2016/12/20',1),
('2016/12/20',4),
('2016/12/20',5),
('2016/12/21',2),
('2016/12/21',6),
('2016/12/21',7),
('2016/12/21',8),
--this is the break in the weeks
('2016/12/26',1),
('2016/12/26',2),
--('2016/12/26',1), -- removed this value since a unique ID shouldn't be allowed to attend twice for a single day
('2016/12/27',2),
('2016/12/27',6),
('2016/12/27',7),
('2016/12/27',8)
--temp table to hold some aggregated data
if object_id('tempdb..#tempT') is not null drop table #tempT
select
y.YR
,y.WK
,y.fetcha
,count(y.recargadorPDV) as attend
,sum(y.CTforWK) as accum
into #tempT
from(
select x.*
from
(select
fetcha
,recargadorPDV
,datepart(yy,fetcha) as YR
,datepart(wk,fetcha) as WK
--the case statment is my way of assigning 1 to each recargadorPDV ONLY once for each week so the running total is correct, ignoring duplicates as you stated
,case when count(recargadorPDV) over (partition by datepart(yy,fetcha), datepart(wk,fetcha), recargadorPDV order by fetcha) = 1 then 1 else 0 end as CTforWK
from #table) x) y
group by
y.YR
,y.WK
,y.fetcha
--see the inital results without the running total
select * from #tempT
--see the final results with the running total
select
a.fetcha
,a.attend
,sum(x.accum) as acum_customers
from #tempT a
inner join #tempT x on x.fetcha <= a.fetcha and x.YR = a.YR and x.WK = a.WK
group by a.fetcha, a.attend
order by a.fetcha
--change back the ##DATEFIRST setting
SET DATEFIRST 7;
I have 2 tables with the following datas in them:-
Company
CompanyId CompanyName
1 Company1
2 Company2
3 Company3
Employees
EmployeeId EmployeeName CompanyId StartDate
1 Employee1 1 12/21/2011
2 Employee2 1 01/20/2012
3 Employee3 2 03/23/2012
4 Employee4 2 07/15/2012
5 Employee5 2 01/20/2013
6 Employee6 3 12/17/2013
Now i want to check, How many people were recruited in the team in the specified month and year? I have the storage table as follows:-
RecruiterIndicator
CompanyId Year Month EmployeeRecruited
1 2011 12 1
1 2012 1 1
2 2012 3 1
2 2012 7 1
2 2013 1 1
3 2013 12 1
This should be a merge stored procedure that should update the data if it is present for the same month year and company and insert if that is not present? The loop would start from a particular date that can be an parameter and it would loop through the current month.
Please help me with this
Thanks
Vishal
SELECT YEAR(StartDate) AS [Year], MONTH(StartDate) AS [Month], COUNT(*) EmpTotal
FROM Employees
GROUP BY YEAR(StartDate), MONTH(StartDate)
If you want to see the Total Employees by company as well you can do something like this
SELECT YEAR(StartDate) AS [Year], MONTH(StartDate) AS [Month]
,C.CompanyName , COUNT(E.EmployeeId) EmpTotal
FROM Employees E INNER JOIN Company C
ON E.CompanyId = C.CompanyId
GROUP BY YEAR(StartDate), MONTH(StartDate) ,C.CompanyName
First of all my result looks like this:
KONTONR
Month
SELSKAPSKODE
BELOP
459611
1
BAGA
156000
459611
2
BAGA
73000
459611
4
BAGA
217000
459611
5
BAGA
136000
459611
1
CIVO
45896
459611
3
CIVO
32498
459611
4
CIVO
9841
330096
1
BAGA
42347
330096
3
BAGA
3695
I'm trying to show month 2 month bookings on several accounts, per account (KONTONR) there are several codes (SELSKAPSKODE) on which bookings are recorded (the sum of the bookings as BELOP). I would like to give an overview of the sum of the bookings (BELOP) per account (KONTONR) per month per code (SELSKAPSKODE). My problem is the codes don't show in a month if no bookings are made on that code. Is there a way to fix this? I understand why the codes don't show, since they're simply not in the table I'm querying. And I suspect that the solution is in making a 'fake' table which I then join (left outer join?) with 'another' table.
I just can't get it to work, I'm pretty new to SQL. Can someone please help?
My query looks like this (I only inserted the 'nested' query to make a set-up for a join, if this makes sense?!):
SELECT TOP (100) PERCENT KONTONR, Month, SELSKAPSKODE, BELOP
FROM (
SELECT SELSKAPSKODE, KONTONR, SKIPS_KODE, MONTH(POSTDATO) AS Month, SUM(BELOP) AS BELOP
FROM dbo.T99_DETALJ
WHERE (POSTDATO >= '2012-01-01') AND (BILAGSART = 0 OR BILAGSART = 2)
GROUP BY SELSKAPSKODE, KONTONR, SKIPS_KODE, MONTH(POSTDATO)
) AS T99_summary
GROUP BY KONTONR, SELSKAPSKODE, Month, BELOP
ORDER BY KONTONR, SELSKAPSKODE, Month
So concluding I would like to 'fill up' the missing months (see table at the start), for instance for account (KONTONR) 459611 month 3 is 'missing'. I would like to show month 3, with the sum of the bookings (BELOP) as '0'. Any help is greatly appreciated, thanks in advance!
You can query a table with the values 1-12 and left outer join your result.
Here is a sample using a table variable instead of your query and a CTE to build a table with numbers.
declare #T table
(
Month int
)
insert into #T values(1)
insert into #T values(1)
insert into #T values(1)
insert into #T values(3)
insert into #T values(3)
;with Months(Month) as
(
select 1
union all
select Month + 1
from Months
where Month < 12
)
select M.Month,
count(T.Month) Count,
isnull(sum(T.Month), 0) Sum
from Months as M
left outer join #T as T
on M.Month = T.Month
group by M.Month
Result:
Month Count Sum
----------- ----------- -----------
1 3 3
2 0 0
3 2 6
4 0 0
5 0 0
6 0 0
7 0 0
8 0 0
9 0 0
10 0 0
11 0 0
12 0 0
if you don't want to do all that you could also modify this: SUM(BELOP) with this:
Sum (case when BELOP is not null then 1 else 0 end)
You can also add in the year if you have a creation date for the interactions you are counting which may be helpful if your interactions span the course of many years.
with Months(Month) as
(
select 1
union all
select Month + 1
from Months
where Month < 12
)
select M.Month, year(CreatedOn) as Year,
count(amount) Count,
isnull(sum(amount), 0) Sum
from Months as M
left outer join Charge as C
on M.Month = (month(CreatedOn))
group by M.Month, year(CreatedOn) order by year(CreatedOn)