Count and divide subquery: SQL Server - sql-server

Here is a table I am reading:
sk_calendar_week | ClientId | Amount
------------------+-----------+-------
2014001 | 1 | 550
2014002 | 2 | 900
2014003 | 3 | 389
2014004 | 4 | 300
Here is the query I'm using:
declare #IniDate as int = 20140610, #EndDate as int = 20150425
select
COUNT(distinct sk_calendar_week) WeekQ,
COUNT(distinct sk_calendar_Month) MonthQ
from
(select
sk_date, sk_calendar_week, sk_calendar_Month,
ClientId, Amount
from
TableA
where
Sk_Date between #IniDate and #EndDate) q1
This query returns:
WeekQ | MonthQ
------+-------
4 | 1
How can I divide the 4 from WeekQ with the Amount (550/4; 900/4; 389/4...), to obtain a result like this?
sk_calendar_week | ClientId | Amount | Division
-------------------+----------+--------+---------
2014001 | 1 | 550 | 137.5
2014002 | 2 | 900 | 225
2014003 | 3 | 389 | 97.25
2014004 | 4 | 300 | 75

You can use your first query to populate a local variable and the use it in the second query like this:
declare #IniDate as int = 20140610,
#EndDate as int = 20150425,
#Week int
select #Week = COUNT(distinct sk_calendar_week)
from TableA where Sk_Date between #IniDate and #EndDate )
Select sk_calendar_week,
ClientId,
Amount,
cast(Amount as decimal(8,2)) / #Week as Divsion
A sub query version would suffer a performance hit, but here is an example:
Select sk_calendar_week,
ClientId,
Amount,
cast(Amount as decimal(8,2)) /
(select COUNT(distinct sk_calendar_week)
from TableA where Sk_Date between #IniDate and #EndDate ) as Divsion

Try with window function:
declare #IniDate as int = 20140610, #EndDate as int = 20150425
select *, amount*1.0/count(*) over() as division
from(
select sk_date
,sk_calendar_week
,sk_calendar_Month
,ClientId
,Amount
from TableA
where Sk_Date between #IniDate and #EndDate
)q1

Related

Filter datetime columns in SQL Server

I have a datetime column that has a 5 min interval between the next data, however I want to see if that column contains any time interval less than 5 mins, particularly 5 secs.
So for example:
one date would read 2018-05-04 19:21:46.000
the next row would read 2018-05-04 19:26:46.000
and 2018-05-04 19:31:46.000.
However, we sometimes get rows that read:
2018-05-04 19:36:46.000
then 2018-05-04 19:36:51.000
then 2018-05-04 19:36:56.000
What SQL script would be best to filter the column to distinguish the erroneous data (the 5 secs interval) from the correct data (5 min interval) especially in a table with thousands of rows?
Hi #Andrea, thanks for that. I have a couple of questions. What does the 'q' stand for? and when i rewrite the query as
SELECT ProductID, MyTimestamp, DATEDIFF(second, xMyTimestamp, MyTimestamp) as DIFFERENCE_IN_SECONDS
FROM (
SELECT *,
Lag(MyTimestamp) OVER (ORDER BY MyTimestamp, ProductID) as xMyTimestamp
FROM TableName
) q
WHERE xMyTimestamp IS NOT NULL and ProductID= 31928
I get this result which doesn't compute the time accurately.
+-----------+-------------------------+-----------------------+
| ProductID | MyTimestamp | DIFFERENCE_IN_SECONDS |
+-----------+-------------------------+-----------------------+
| 31928 | 2017-03-21 13:36:30.000 | 0 |
| 31928 | 2017-03-21 13:46:30.000 | 0 |
| 31928 | 2017-03-21 13:56:32.000 | 0 |
| 31928 | 2017-03-21 14:01:32.000 | 0 |
| 31928 | 2017-03-21 14:11:32.000 | 0 |
| 31928 | 2017-03-21 14:16:32.000 | 0 |
| 31928 | 2017-03-21 14:26:32.000 | 0 |
| 31928 | 2017-03-21 14:36:32.000 | 0 |
+-----------+-------------------------+-----------------------+
Any reason why
Since you are on 2014, you can use LEAD to compare the value of one row, to the value of the next.
declare #table table(id int identity(1,1), interval datetime)
insert into #table
values
('2018-05-04 19:21:46.000'),
('2018-05-04 19:26:46.000'),
('2018-05-04 19:31:46.000'),
('2018-05-04 19:36:46.000'),
('2018-05-04 19:36:51.000'),
('2018-05-04 19:36:56.000')
select
id
,interval
,issue_with_row = case
when
isnull(datediff(minute,interval,lead(interval) over (order by id, interval)),0) < 5
then 1
else 0
end
from #table
order by id
Or if you wanted to only see those,
;with cte as(
select
id
,interval
,issue_with_row = case
when
isnull(datediff(minute,interval,lead(interval) over (order by id, interval)),0) < 5
then 1
else 0
end
from #table)
select *
from cte
where issue_with_row = 1
You can use LAG:
declare #tmp table(MyTimestamp datetime)
insert into #tmp values
('2018-05-04 19:21:46.000')
,('2018-05-04 19:26:46.000')
,('2018-05-04 19:31:46.000')
,('2018-05-04 19:36:46.000')
,('2018-05-04 19:36:51.000')
,('2018-05-04 19:36:56.000')
SELECT DATEDIFF(second, xMyTimestamp, MyTimestamp) as DIFFERENCE_IN_SECONDS
FROM (
SELECT *,
LAG(MyTimestamp) OVER (ORDER BY MyTimestamp) xMyTimestamp
FROM #tmp
) q
WHERE xMyTimestamp IS NOT NULL
results:
So you should use it like this:
SELECT DATEDIFF(second, xMyTimestamp, MyTimestamp) as DIFFERENCE_IN_SECONDS
FROM (
SELECT *,
LAG(MyTimestamp) OVER (ORDER BY MyTimestamp) xMyTimestamp
FROM [YOUR_TABLE_NAME_HERE]
) q
WHERE xMyTimestamp IS NOT NULL
Edit
Here is another sample based on new data posted by OP:
declare #tmp table(ProductID int, MyTimestamp datetime)
insert into #tmp values
(31928, '2017-03-21 13:36:30.000')
,(31928, '2017-03-21 13:46:30.000')
,(31928, '2017-03-21 13:56:32.000')
,(31928, '2017-03-21 14:01:32.000')
,(31928, '2017-03-21 14:11:32.000')
,(31928, '2017-03-21 14:16:32.000')
,(31928, '2017-03-21 14:26:32.000')
,(31928, '2017-03-21 14:36:32.000')
SELECT ProductID
,MyTimestamp
,DATEDIFF(second, xMyTimestamp, MyTimestamp) AS DIFFERENCE_IN_SECONDS
FROM (
SELECT *
,Lag(MyTimestamp) OVER (
ORDER BY MyTimestamp
,ProductID
) AS xMyTimestamp
FROM #tmp
) q
WHERE xMyTimestamp IS NOT NULL
AND ProductID = 31928
Output:
Here you can check that the results are calculated correctly.

SQL Find Tickets Open Between Start Date and End Date

I currently have the following table:
+-----+-----------------------------+------------------------------+
| ID | StartDate | EndDate |
+-----+-----------------------------+------------------------------|
| 1 | 2017-07-24 08:00:00.000 | 2017-07-29 08:00:00.000 |
| 2 | 2017-07-25 08:00:00.000 | 2017-07-28 08:00:00.000 |
| 3 | 2017-07-25 08:00:00.000 | 2017-07-26 08:00:00.000 |
+-----+-----------------------------+------------------------------+
I would like to know the count of the ID's that were not Closed on each date.
So for example, I wan't to know the count of open ID's on 2017-07-26 00:00:00.000. This would be all 3 in this case.
Another example: I wan't to know the count of open ID's on 2017-07-29 00:00:00.000. Which would be result to 1. Only ID=1 is Not yet closed at that date.
I have tried using another solution here on StackOverflow, but I can't quite figure why it is giving me false results.
declare #dt date, #dtEnd date
set #dt = getdate()-7
set #dtEnd = dateadd(day, 100, #dt);
WITH CTEt1 (SupportCallID, StartDate, EndDate, Onhold)
as
(SELECT SupportCallID
,OpenDate
,MAX(CASE WHEN StatusID IN('19381771-8E81-40C5-8E36-62A7DB0A2A99', '95C7A5FB-2389-4D14-9DAE-A08BFCC3B09A', 'D5429790-3B43-4462-9E1E-2466EA29AC74') then CONVERT(DATE, LastChangeDate) end) EndDate
,OnHold
FROM [ClienteleITSM_Prod_Application].[dbo].[SupportCall]
group by SupportCallID, OpenDate, OnHold
)
SELECT dates.myDate,
(SELECT COUNT(*)
FROM CTEt1
WHERE myDate BETWEEN StartDate and EndDate
)
FROM
(select dateadd(day, number, #dt) mydate
from
(select distinct number from master.dbo.spt_values
where name is null
) n
where dateadd(day, number, #dt) < #dtEnd) dates
If you use a cte to create a table of dates that span the range of dates in your source table, you can easily left join from that to your source table and count up the rows returned:
declare #t table(ID int,StartDate datetime,EndDate datetime);
insert into #t values (1,'2017-07-24 08:00:00.000','2017-07-29 08:00:00.000'),(2,'2017-07-25 08:00:00.000','2017-07-28 08:00:00.000'),(3,'2017-07-25 08:00:00.000','2017-07-26 08:00:00.000');
declare #StartDate datetime = (select min(StartDate) from #t);
declare #EndDate datetime = (select max(EndDate) from #t);
-- Table with 10 rows in to be joined together to create a large tally table (10 * 10 * 10 * etc)
with t(t) as (select t from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(t))
-- Add the row_number of the tally table to your start date to generate all dates within your data range
,d(d) as (select top(datediff(d,#StartDate,#EndDate)+1) dateadd(d,row_number() over (order by (select null))-1,#StartDate) from t t1,t t2,t t3)
select d.d
,count(t.ID) as OpenIDs
from d
left join #t as t
on(d.d between cast(t.StartDate as date) and t.EndDate)
group by d.d
order by d.d;
Output:
+-------------------------+---------+
| d | OpenIDs |
+-------------------------+---------+
| 2017-07-24 08:00:00.000 | 1 |
| 2017-07-25 08:00:00.000 | 3 |
| 2017-07-26 08:00:00.000 | 3 |
| 2017-07-27 08:00:00.000 | 2 |
| 2017-07-28 08:00:00.000 | 2 |
| 2017-07-29 08:00:00.000 | 1 |
+-------------------------+---------+

SQL Loop form between two days with ID

I Want to create loop between two days and USERID
For Example
If I Choose between 01-01-2017 and 05-01-2017
procedure already insert into my table days between for each user
every single user has a same days
I've tried the following
DECLARE #USERID int,#S_Date date,#E_Date date
SELECT #S_Date = #S_Date, #USERID = #USERID
While #S_Date <= #E_Date
begin
select #S_Date, #USERID
while #USERID < 3
begin
INSERT INTO myTable values(#S_Date, #USERID)
select #S_Date as S, #USERID as U
set #USERID = #USERID + 1
end
set #S_Date = DateAdd(Day, 1, #S_Date)
end
declare #fromdate date = '20170101'
declare #thrudate date = '20170105'
declare #from_UserId int = 1;
declare #thru_UserId int = 3;
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, dates as (
select top (datediff(day, #fromdate, #thrudate)+1)
[Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,#fromdate))
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by [Date]
)
insert into myTable ([Date],UserId)
select [Date], u.UserId
from dates
cross join (
select UserId
from Users u
where u.UserId >= #from_UserId
and u.UserId <= #thru_UserId
) u;
The above uses stacked ctes to generate a numbers table, and uses that numbers table to generate dates between the date range provided.
It might not look as simple as using a while loop, but it will perform much better.
rextester demo: http://rextester.com/HHY62656
returns:
+------------+--------+
| date | userid |
+------------+--------+
| 2017-01-01 | 1 |
| 2017-01-02 | 1 |
| 2017-01-03 | 1 |
| 2017-01-04 | 1 |
| 2017-01-05 | 1 |
| 2017-01-01 | 2 |
| 2017-01-02 | 2 |
| 2017-01-03 | 2 |
| 2017-01-04 | 2 |
| 2017-01-05 | 2 |
| 2017-01-01 | 3 |
| 2017-01-02 | 3 |
| 2017-01-03 | 3 |
| 2017-01-04 | 3 |
| 2017-01-05 | 3 |
+------------+--------+
Number and Calendar table reference:
Generate a set or sequence without loops - 2 - Aaron Bertrand
The "Numbers" or "Tally" Table: What it is and how it replaces a loop - Jeff Moden
Creating a Date Table/Dimension in sql Server 2008 - David Stein
Calendar Tables - Why You Need One - David Stein
Creating a date dimension or calendar table in sql Server - Aaron Bertrand

SQL - how do I generate rows for each month based on date ranges in existing dataset?

assume I have a dataset:
rowID | dateStart | dateEnd | Year | Month
121 | 2013-10-03 | 2013-12-03 | NULL | NULL
143 | 2013-12-11 | 2014-03-11 | NULL | NULL
322 | 2014-01-02 | 2014-02-11 | NULL | NULL
And I want sql to generate the following datasource based on the dateStart and the dateEnd. Note the year and month grouping.
rowID | dateStart | dateEnd | Year | Month
121 | 2013-10-03 | 2013-12-03 | 2013 | 10
121 | 2013-10-03 | 2013-12-03 | 2013 | 11
121 | 2013-10-03 | 2013-12-03 | 2013 | 12
143 | 2013-12-11 | 2014-03-11 | 2013 | 12
143 | 2013-12-11 | 2014-03-11 | 2014 | 1
143 | 2013-12-11 | 2014-03-11 | 2014 | 2
143 | 2013-12-11 | 2014-03-11 | 2014 | 3
322 | 2014-01-02 | 2014-02-11 | 2014 | 1
322 | 2014-01-02 | 2014-02-11 | 2014 | 2
I'm having a hard time wrapping my head around this one. Any ideas?
I find it easiest to approach these problems by creating a list of integers and then using that to increment the dates. Here is an example:
with nums as (
select 0 as n
union all
select n + 1 as n
from nums
where n < 11
)
select rowid, datestart, dateend,
year(dateadd(month, n.n, datestart)) as yr,
month(dateadd(month, n.n, datestart)) as mon
from table t join
nums n
on dateadd(month, n.n - 1, datestart) <= dateend;
First, create a tabled-valued function that takes the 2 dates and returns the year and month as a table:
create function dbo.YearMonths(#StartDate DateTime, #EndDate DateTime)
returns #YearMonths table
([Year] int,
[Month] int)
as
begin
set #EndDate = DATEADD(month, 1, #EndDate)
while (#StartDate < #EndDate)
begin
insert into #YearMonths
select YEAR(#StartDate), MONTH(#StartDate)
set #StartDate = DATEADD(month, 1, #StartDate)
end
return
end
As an example the following:
select *
from dbo.YearMonths('1/1/2014', '5/1/2014')
returns:
Then you would join to it like this to get what you wanted:
select m.*, ym.Year, ym.Month
from myTable m
cross apply dbo.YearMonths(dateStart, dateEnd) ym
Try this:
declare #months table(mth int)
insert into #months values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
declare #calendar table(yr int,mth int)
insert into #calendar
select distinct year(datestart),mth
from tbl cross join #months
union
select distinct year(dateend),mth
from tbl cross join #months
select t.rowID, t.datestart, t.dateend, y.yr [Year], y.mth [Month]
from
yourtable t
inner join #calendar y on year(datestart) = yr or year(dateend) = yr
where
(mth >= month(datestart) and mth <= month(dateend) and year(datestart) = year(dateend))
or
(year(datestart) < year(dateend))
and
(year(datestart) = yr and mth >= month(datestart) --All months of start year
or
(year(dateend) = yr and mth <= month(dateend))) -- All months of end year
order by t.rowID, [Year],[Month]
We create a 'Calendar table' which lists all the month and year combinations present in the source table. Then, we join the source table to the calendar table based on the year, and filter as required.

How do i can show forecast years data from row into column?

Suppose if Item-A's Sale in 2013 is 100 Quantity and I'm expecting 10% of sales growth in next year i.e. in 2014
--------------------
ITEM | YEAR | QTY |
--------------------
ITM-A| 2013 | 100 |
ITM-B| 2013 | 200 |
--------------------
if I want to forecast sale data for up to year 2015
------------------------------
Item | 2013 | 2014 | 2015 |
------------------------------
Item-A | 100 | 110 | 121 |--each year qty incremented by 10% of its
Item-B | 200 | 220 | 242 |--previous year qty
------------------------------
try this,you have to use dynamic sql
Declare #toyear int=2016
Declare #forcast int=10
Declare #t table (ITEM varchar(50), years int, qty int)
insert into #t
select 'TM-A' ITEM , 2013 years, 100 qty
union all
select 'TM-B' ITEM , 2013 years, 200 qty
;with CTE1 as
(
select * from #t
union all
select b.ITEM,b.years+1,b.qty+((#forcast*b.qty)/100) from #t a
inner join cte1 b on a.ITEM=b.ITEM
and b.years<#toyear
)
select * from
(select * from cte1 )t4
pivot(min(qty) for years in([2013],[2014],[2015],[2016]))pvt
Try this:
select item,
qty as '2013',
round(qty*1.1) as '2014',
round(qty*1.21) as '2015'
from sale;
A dynamic query using stored procedure
DELIMITER $$
create procedure p (IN end INT(10))
BEGIN
declare start int;
declare fact FLOAT;
SET fact = 1.1;
SELECT year into start FROM sale limit 1;
SET #QUERY1 = CONCAT("SELECT ITEM, QTY AS '",start,"'");
WHILE start < end DO
SET start = start + 1;
SET #QUERY1 = CONCAT(#QUERY1," ,qty*",fact," as '", start,"'");
SET fact = fact *1.1;
END WHILE;
SET #QUERY1 = CONCAT(#QUERY1," from sale");
PREPARE stmt FROM #QUERY1;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END $$
DELIMITER ;
Output:
mysql> call p(2016);
+-------+------+-------+--------+---------+
| ITEM | 2013 | 2014 | 2015 | 2016 |
+-------+------+-------+--------+---------+
| itemA | 100 | 110.0 | 121.00 | 133.100 |
| itemB | 200 | 220.0 | 242.00 | 266.200 |
+-------+------+-------+--------+---------+
2 rows in set (0.00 sec)
Check this question:
Pass a function return to another in the same row
Your function is simply the multiplication by 1.1

Resources