How do I SELECT all the entries in a SQL table that have a date within the last week but only if the EQNum has not appeared in the past 6 months - sql-server

I am very much a SQL novice. I am looking to write a script that will select all the columns from a table where two criteria are met:
The date of the call must have happened within the past 7 days
The EQNum must not have had another call placed on it in the past six months
Here is a sample table:
Call, Date, EQNum, Customer
123, 06-16-2015, 75, ABC Co
125, 06-16-2015, 82, XYZ Co
133, 06-14-2015, 69, DEF Co
101, 05-12-2015, 82, XYZ Co
115, 10-11-2014, 69, DEF Co
The query I need created should return:
123, 06-16-2015, 75, ABC Co
133, 06-14-2015, 69, DEF Co
The Call 125 (EQNum 82) is eliminated because though is occurred in the past week, EQNum 82 had another call (Call 101) occur within the last 6 month thus eliminating it.
Call 133 is valid because the other call for EQNum 69 occurred more than 6 months ago.

Something like this:
SELECT *
from tbl
WHERE DateCol > DATEADD(day, -7, getdate())
AND NOT EXISTS (SELECT TOP 1 1
FROM tbl this
WHERE this.EQNum = tbl.EQNum
AND this.DateCol > DATEADD(month, -6, getdate())
)

This is one way, although it probably wouldn't perform well if the table got massive
select
Call,
[Date],
EQNum,
Customer
from #table
where
[Date] > getdate() - 7 and
EQNum not in
(
select
EQNum
from #table
where
[Date] > DATEADD(month, -6, getdate())
group by
EQNum
having count(*) > 1
)
Another way would be to left join...
select
Call,
[Date],
EQNum,
Customer
from #table t1
left join #table t2 on
t1.Call != t2.Call and
t1.EQNum = t2.EQNum and
t2.Date > DATEADD(month, -6, getdate())
where
t1.[Date] > getdate() - 7 and
t2.Call is null

Related

Create SQL Pivot Table Depending On Different Periods of times

I want to create pivot table for data of the due values of different customers, and i want to pivot my data to 3 pivoted periods of time Such that the first Column has the total notes of the period from today till 30 days from now in the future and the second one for the values due in the period of ((Now + 30)< Due <60)
and the next one has values of ((Now+ 60) < Due < 90) and the last one has the value due today.
This's My Code which get my raw data:
SELECT [ADD].AccountID
,SUM(convert(money,ADDN.Amount - ISNULL(CollectedValue,0))) AS [Total Rest Amount]
,ADDN.DueDate AS [Due Date]
FROM [Accounting].[AccDocumentDetailsNotes] ADDN
INNER JOIN Accounting.AccDocumentDetails [ADD]
ON ADDN.AccDocumentDetailID = [ADD].ID
INNER JOIN Accounting.AccDocumentHeader ADH
ON ADH.ID = [ADD].AccDocumentHeaderID
INNER JOIN [Accounting].[AccNotesCollectors] ANC
ON ANC.NoteID = ADDN.ID
INNER JOIN Accounting.AccAccounts AA
ON AA.ID = [ADD].AccountID
GROUP BY [ADD].AccountID,ADDN.DueDate,[CodeTypePart],ADDN.Amount,CollectedValue
HAVING [CodeTypePart] = 'NR' AND convert(money,ADDN.Amount - ISNULL(CollectedValue,0)) > 0
And This's a Historical sample from the result:
AccountID Total Rest Amount Due Date
----------- --------------------- -----------------------
25 6800.00 2017-02-23 17:31:09.000
25 1700.00 2017-02-23 17:31:09.000
25 10602.00 2017-05-28 16:28:14.000
27 14500.00 2017-02-28 14:53:57.000
30 120150.00 2017-02-24 00:23:20.000
30 117050.00 2017-02-24 00:23:20.000
33 2000.00 2017-04-04 20:48:51.193
45 39500.00 2017-04-18 20:13:46.000
45 31300.00 2017-04-18 20:13:46.000
45 9000.00 2017-04-18 20:13:46.000
45 32200.00 2017-04-22 16:38:47.803
46 32500.00 2017-02-23 20:14:24.000
46 15910.00 2017-02-23 20:14:24.000
And I want to seems as:
So you need to break down your data into groups by how overdue it is, and then pivot on that. Then to get the total, you can add together all the sub-columns.
select
AccountID,
isnull([90+],0)+isnull( [today 61-90],0)+ isnull( [today 31-60],0)+isnull( [today-30],0) total,
[90+], [today 61-90], [today 31-60], [today-30]
from
(
select AccountId, Amount,
CASE
WHEN datediff(d, duedate, getdate()) <= 30 THEN 'today-30'
when datediff(d, duedate, getdate()) between 31 and 60 then 'today 31-60'
when datediff(d, duedate, getdate()) between 61 and 90 then 'today 61-90'
else '90+'
END as daysoverdue
from #t
) src
pivot
( sum(Amount) for daysoverdue in ([90+], [today 61-90], [today 31-60], [today-30] ))p
Try this:
;with data as (
select
Today = cast(getdate() as date),
Plus30 = dateadd(d, 30, cast(getdate() as date) ),
Plus60 = dateadd(d, 60, cast(getdate() as date) ),
Plus90 = dateadd(d, 90, cast(getdate() as date) ),
EndOfTime = cast('21991231' as date),
t.*
from #t as t
)
select
AccountId,
Total = sum(Amount),
Due0To30 = sum(pvt.Due0To30),
Due31To60 = sum(pvt.Due31To60),
Due61To90 = sum(pvt.Due61To90),
Due91Plus = sum(pvt.Due91Plus)
from data
cross apply (values
(Today, Plus30, Amount, 0, 0, 0),
(Plus30, Plus60, 0, Amount, 0, 0),
(Plus60, Plus90, 0, 0, Amount, 0),
(Plus90, EndOfTime, 0, 0, 0, Amount)
)pvt(StartDate,EndDate,Due0To30, Due31To60, Due61To90, Due91Plus)
where [Due Date] >= pvt.StartDate
and [Due Date] < pvt.EndDate
group by AccountID

SQL Select Sequential Dates with Additional Lookup Values

I am trying to grab a series of dates and the corresponding values (if any) that exist in my database.
I have two parameters - today (date using getDate()) - and a number of days (integer). For this example, I'm using the value 10 for the days.
Code to get the sequential dates for 10 days after today:
SELECT top 10 DATEADD(DAY, ROW_NUMBER()
OVER (ORDER BY object_id), REPLACE(getDate(),'-','')) as Alldays
FROM sys.all_objects
I now need to look up several values for each day in the sequential days code, which may or may not exist in the time table (we assume 8 hours for all dates, unless otherwise specified). The lookup would be on the field recordDateTime. If no "hours" value exists in the table cap_time for that date, I need to return a default value of 8 as the number of hours. Here's the base query:
SELECT u.FullName as UserName, d2.department,
recordDateTime, ISNULL(hours,8) as hours
FROM cap_time c
left join user u on c.userID = u.userid
left join dept d2 on u.deptID = d2.DeptID
WHERE c.userid = 38 AND u.deptID = 1
My end result for the next 10 days should be something like:
Date (sequential), Department, UserName, Number of Hours
I can accomplish this using TSQL and a temp table, but I'd like to see if this can be done in a single statement. Any help is appreciated.
Without any DDL or sample data it's hard to determine exactly what you need.
I think this will get you pretty close (note my comments):
-- sample data
------------------------------------------------------------------------------------------
DECLARE #table TABLE
(
fullName varchar(10),
department varchar(10),
[hours] tinyint,
somedate date
);
INSERT #table VALUES
('bob', 'sales', 5, getdate()+1),
('Sue', 'marketing', 3, getdate()+2),
('Sue', 'sales', 12, getdate()+4),
('Craig', 'sales', 4, getdate()+8),
('Joe', 'sales', 18, getdate()+9),
('Fred', 'sales', 10, getdate()+10);
--SELECT * FROM #table
;
-- solution
------------------------------------------------------------------------------------------
WITH alldays([day]) AS -- logic to get your dates for a LEFT date table
(
SELECT TOP (10)
CAST(DATEADD
(
DAY,
ROW_NUMBER() OVER (ORDER BY object_id),
getdate()
) AS date)
FROM sys.all_objects
)
SELECT d.[day], t.fullName, department, [hours] = ISNULL([hours], 8)
FROM alldays d
LEFT JOIN #table t ON d.[day] = t.somedate;
Results:
day fullName department hours
---------- ---------- ---------- -----
2017-04-12 bob sales 5
2017-04-13 Sue marketing 3
2017-04-14 NULL NULL 8
2017-04-15 Sue sales 12
2017-04-16 NULL NULL 8
2017-04-17 NULL NULL 8
2017-04-18 NULL NULL 8
2017-04-19 Craig sales 4
2017-04-20 Joe sales 18
2017-04-21 Fred sales 10
Maybe a subquery and the in statement, like:
SELECT u.FullName as UserName, d2.department,
recordDateTime, ISNULL(hours,8) as hours
FROM cap_time c
left join user u on c.userID = u.userid
left join dept d2 on u.deptID = d2.DeptID
WHERE c.userid = 38 AND u.deptID = 1 and recordDateTime in
(SELECT top 10 DATEADD(DAY, ROW_NUMBER()
OVER (ORDER BY object_id), REPLACE(getDate(),'-','')) as Alldays
FROM sys.all_objects)

How get one line per key in left outer join

The context is a transaction table with date and UserAccount. This table contains about billion lines.
dOperationValueDate sUserAccount
------------------- ----------------------------------------------
2016-03-05 00000000001
2016-03-06 00000000002
2016-03-07 00000000003
2016-03-08 00000000004
2016-03-09 00000000005
2016-04-05 00000000002
2016-10-05 00000000001
2016-10-06 00000000001
2016-10-06 00000000005
I would like to find datas in my table with these criterias :
At least one transaction before 6 months ago (like TOP 1 *)
No transaction for 6 months
In my example, the results would be accounts 2, 3, 4.
I started with a LEFT OUTER JOIN, in order to remove all userId with transaction since 6 months. But the processing time is just horrible : for 4 hours right now.
SELECT b.sUserAccount FROM
(SELECT sUserAccount FROM T_Operations WITH (readuncommitted) WHERE dOperationValueDate < DATEADD(month, -6, DATEADD(month, DATEDIFF(month, 0, GETUTCDATE()), 0)) GROUP BY sUserAccount) b -- all operations before 6 months ago
LEFT JOIN
(SELECT sUserAccount FROM T_Operations WITH (readuncommitted) WHERE dOperationValueDate >= DATEADD(month, -6, DATEADD(month, DATEDIFF(month, 0, GETUTCDATE()), 0)) GROUP BY sUserAccount) c -- all operations since 6 months
ON b.sUserAccount = c.sUserAccount
WHERE c.sUserBankAccount IS NULL) d -- remove all customers who have operations before 6 months ago and since 6 months / keep only customers who have operations beofre 6 months ago only
I think the solution is to find only one operation in the b query, and sql stops when it find one row. The main problem is only if the user doesn't have transaction before 6 months ago but for the others, it will be fine.
On the other hand, I have to check each transaction since 6 months in order to remove customers from the scope.
I read about CROSS APPLY, but I'm not sure about how it works.
The main problem here is the processing time. I have to do a "quick" request (less than 1 hour).
I think you should be able to just use NOT EXISTS here.
SELECT b.sUserAccount
FROM T_Operations b WITH (READUNCOMMITTED)
WHERE b.dOperationValueDate < DATEADD(month,-6,DATEADD(month,DATEDIFF(month,0,GETUTCDATE()),0))
AND NOT EXISTS ( SELECT 1
FROM T_Operations WITH (READUNCOMMITTED)
WHERE sUserAccount = b.sUserAccount
AND dOperationValueDate >= DATEADD(month,-6,DATEADD(month,DATEDIFF(month,0,GETUTCDATE()),0)) )
GROUP BY b.sUserAccount -- all operations before 6 months ago
or actually, you might be able to just use GROUP BY with HAVING
SELECT sUserAccount
FROM T_Operations WITH (READUNCOMMITTED)
GROUP BY sUserAccount
HAVING MAX(dOperationValueDate) < DATEADD(month,-6,DATEADD(month,DATEDIFF(month,0,GETUTCDATE()),0))
as a side note.. DATEADD(month,-6,DATEADD(month,DATEDIFF(month,0,GETUTCDATE()),0)) would return 2016-04-01
if you want current date, minus six months you can use DATEADD(month,-6,CAST(GETUTCDATE() AS DATE)) or DATEADD(month,-6,DATEADD(day,DATEDIFF(day,0,GETUTCDATE()),0)
datatime #dt = DATEADD(month, -6, DATEADD(month, DATEDIFF(month, 0, GETUTCDATE()), 0));
SELECT sUserAccount
FROM T_Operations WITH (readuncommitted)
WHERE dOperationValueDate < #dt
EXCEPT
SELECT sUserAccount
FROM T_Operations WITH (readuncommitted)
WHERE dOperationValueDate >= #dt;
Have an index on dOperationValueDate

How to calculate overlapping subscription days from orders with sql-server

I have an ordertable with orders. I want to calculate the amount of subscriptiondays for each user (preffered in a set-based way) for a specific day.
create table #orders (orderid int, userid int, subscriptiondays int, orderdate date)
insert into #orders
select 1, 2, 10, '2011-01-01'
union
select 2, 1, 10, '2011-01-10'
union
select 3, 1, 10, '2011-01-15'
union
select 4, 2, 10, '2011-01-15'
declare #currentdate date = '2011-01-20'
--userid 1 is expected to have 10 subscriptiondays left
(since there is 5 left when the seconrd order is placed)
--userid 2 is expected to have 5 subscriptionsdays left
I'm sure this has been done before, I just dont know what to search for.
Pretty much like a running total?
So when I set #currentdate to '2011-01-20' I want this result:
userid subscriptiondays
1 10
2 5
When I set #currentdate to '2011-01-25'
userid subscriptiondays
1 5
2 0
When I set #currentdate to '2011-01-11'
userid subscriptiondays
1 9
2 0
Thanks!
I think you would need to use a recursive common table expression.
EDIT: I've also added a procedural implementation further below instead of using a recursive common table expression. I recommend using that procedural approach, as I think there may be a number of data scenarios that the recursive CTE query that I've included probably doesn't handle.
The query below gives the correct answers for the scenarios that you've provided, but you would probably want to think up some additional complex scenarios and see whether there are any bugs.
For instance, I have a feeling that this query may break down if you have multiple previous orders overlapping with a later order.
with CurrentOrders (UserId, SubscriptionDays, StartDate, EndDate) as
(
select
userid,
sum(subscriptiondays),
min(orderdate),
dateadd(day, sum(subscriptiondays), min(orderdate))
from #orders
where
#orders.orderdate <= #currentdate
-- start with the latest order(s)
and not exists (
select 1
from #orders o2
where
o2.userid = #orders.userid
and o2.orderdate <= #currentdate
and o2.orderdate > #orders.orderdate
)
group by
userid
union all
select
#orders.userid,
#orders.subscriptiondays,
#orders.orderdate,
dateadd(day, #orders.subscriptiondays, #orders.orderdate)
from #orders
-- join any overlapping orders
inner join CurrentOrders on
#orders.userid = CurrentOrders.UserId
and #orders.orderdate < CurrentOrders.StartDate
and dateadd(day, #orders.subscriptiondays, #orders.orderdate) > CurrentOrders.StartDate
)
select
UserId,
sum(SubscriptionDays) as TotalSubscriptionDays,
min(StartDate),
sum(SubscriptionDays) - datediff(day, min(StartDate), #currentdate) as RemainingSubscriptionDays
from CurrentOrders
group by
UserId
;
Philip mentioned a concern about the recursion limit on common table expressions. Below is a procedural alternative using a table variable and a while loop, which I believe accomplishes the same thing.
While I've verified that this alternative code does work, at least for the sample data provided, I'd be glad to hear anyone's comments on this approach. Good idea? Bad idea? Any concerns to be aware of?
declare #ModifiedRows int
declare #CurrentOrders table
(
UserId int not null,
SubscriptionDays int not null,
StartDate date not null,
EndDate date not null
)
insert into #CurrentOrders
select
userid,
sum(subscriptiondays),
min(orderdate),
min(dateadd(day, subscriptiondays, orderdate))
from #orders
where
#orders.orderdate <= #currentdate
-- start with the latest order(s)
and not exists (
select 1
from #orders o2
where
o2.userid = #orders.userid
and o2.orderdate <= #currentdate
-- there does not exist any other order that surpasses it
and dateadd(day, o2.subscriptiondays, o2.orderdate) > dateadd(day, #orders.subscriptiondays, #orders.orderdate)
)
group by
userid
set #ModifiedRows = ##ROWCOUNT
-- perform an extra update here in case there are any additional orders that were made after the start date but before the specified #currentdate
update co set
co.SubscriptionDays = co.SubscriptionDays + #orders.subscriptiondays
from #CurrentOrders co
inner join #orders on
#orders.userid = co.UserId
and #orders.orderdate <= #currentdate
and #orders.orderdate >= co.StartDate
and dateadd(day, #orders.subscriptiondays, #orders.orderdate) < co.EndDate
-- Keep attempting to update rows as long as rows were updated on the previous attempt
while(#ModifiedRows > 0)
begin
update co set
SubscriptionDays = co.SubscriptionDays + overlap.subscriptiondays,
StartDate = overlap.orderdate
from #CurrentOrders co
-- join any overlapping orders
inner join (
select
#orders.userid,
sum(#orders.subscriptiondays) as subscriptiondays,
min(orderdate) as orderdate
from #orders
inner join #CurrentOrders co2 on
#orders.userid = co2.UserId
and #orders.orderdate < co2.StartDate
and dateadd(day, #orders.subscriptiondays, #orders.orderdate) > co2.StartDate
group by
#orders.userid
) overlap on
overlap.userid = co.UserId
set #ModifiedRows = ##ROWCOUNT
end
select
UserId,
sum(SubscriptionDays) as TotalSubscriptionDays,
min(StartDate),
sum(SubscriptionDays) - datediff(day, min(StartDate), #currentdate) as RemainingSubscriptionDays
from #CurrentOrders
group by
UserId
EDIT2: I've made some adjustments to the code above to address various special cases, such as if there just happen to be two orders for a user that both end on the same date.
For instance, changing the setup data to the following caused issues with the original code, which I've now corrected:
insert into #orders
select 1, 2, 10, '2011-01-01'
union
select 2, 1, 10, '2011-01-10'
union
select 3, 1, 10, '2011-01-15'
union
select 4, 2, 6, '2011-01-15'
union
select 5, 2, 4, '2011-01-17'
EDIT3: I've made some additional adjustments to address other special cases. In particular, the previous code ran into issues with the following setup data, which I've now corrected:
insert into #orders
select 1, 2, 10, '2011-01-01'
union
select 2, 1, 6, '2011-01-10'
union
select 3, 1, 10, '2011-01-15'
union
select 4, 2, 10, '2011-01-15'
union
select 5, 1, 4, '2011-01-12'
If my clarifying comment/question is correct, then you want to use DATEDIFF:
DATEDIFF(dd, orderdate, #currentdate)
My interpretation of the problem:
On day X, customer buys a “span” of subscription days (i.e. good for N days)
The span starts on the day of purchase and is good for X through day X + (N - 1)... but see below
If customer purchases a second span after the first expires (or any new span after all existing spans expire), repeat process. (A single 10-day purchase 30 days ago has no impact on a second purhcase made today.)
If customer purchases a span while existing span(s) are still in effect, the new span applies to day immediately after end of current span(s) through that date + (N – 1)
This is iterative. If customer buys 10-day spans on Jan 1st, Jan 2nd, and Jan 3rd, it would look something like:
As of 1st: Jan 1 – Jan 10
As of 2nd: Jan 1 – Jan 10, Jan 11 – Jan 20 (in effect, Jan 1 to Jan 20)
As of 3rd: Jan 1 – Jan 10, Jan 11 – Jan 20, Jan 21 – Jan 30 (in effect, Jan 1 to Jan 30)
If this is indeed the problem, then it is a horrible problem to solve in T-SQL. To deterimine the “effective span” of a given purchase, you have to calculate the effective span of all prior purchases in the order that they were purchased, because of that overall cumulative effect. This is a trivial problem with 1 user and 3 rows, but non-trivial with thousands of users with dozens of purchases (which, presumably, is what you want).
I would solve it like so:
Add column EffectiveDate of datatype date to the table
Build a one-time process to walk through every row user-by-user and orderdate by orderdate, and calculate the EffectiveDate as discussed above
Modify the process used to insert the data to calculate the EffectiveDate at the time a new entry is made. Done this way, you’d only ever have to reference the most recent purchase made by that user.
Wrangle out subsequent issues regarding deleting (cancelled?) or updating (mis-set?) orders
I may be wrong, but I don't see any way to address this using set-based tactics. (Recursive CTEs and the like would work, but they can only recurse to so many levels, and we don't know the limit for this problem -- let alone how often you'll need to run it, or how well it must perform.) I'll watch and upvote anyone who solves this without recursion!
And of course this only applies if my understanding of the problem is correct. If not, please disregard.
In fact, we need calculate summ of subscriptiondays minus days beetwen first subscrible date and #currentdate like:
select userid,
sum(subsribtiondays)-
DATEDIFF('dd',
(select min(orderdate)
from #orders as a
where a.userid=userid), #currentdate)
from #orders
where orderdate <= #currentdata
group by userid

multiple aggregate functions with multiple tables in SQL Server

I am trying to get data for entered person, I want to pull out data as No of invoices and No of line items for particular person.
The output is
Entered_by No of line items
CD 9
CD 136084
deepa 7
deepa 18
dolly 757
dolly 22350
kroshni 666
kroshni 16161
lokesh 4
lokesh 999
MHeera 639
MHeera 20427
nandini 7
nandini 5318
Here the data in No of line items is mixing of both ’ No of line items’ count and ‘No of invoices’ count, I want to show like
Entered_by No of line items No of invoices
CD 136084 9
deepa 18 7
dolly 22350 757
Please help me with this somebody ….. 
Here is the T-SQL query
select ENTERED_BY, count(entered_by) 'NO OF LINE ITEMS'
from im_invoice, im_invoice_line_item, im_invoice_inventory
where invoice_rid = invoice_fk
and invoice_inventory_rid = invoice_inv_fk
and enter_date between dateadd(mm, -3, getdate()) and dateadd(mm,0,getdate())
group by entered_by
union
select entered_by, count(invoice_num) 'NO OF INVOICES' from im_invoice
where enter_date between dateadd(mm, -3, getdate()) and dateadd(mm,0,getdate())
group by entered_by
As Joe said, if you give us a more detailed description we can give you better answers, but until then, quick and dirty way to accomplish this is as follows:
Get rid of the union
Turn the 2 queries into derived tables
Select from them joining on entered_by.
Eg.
SELECT LineItems.ENTERED_BY, [NO OF LINE ITEMS], [NO OF INVOICES]
FROM
(SELECT ENTERED_BY,COUNT(entered_by) 'NO OF LINE ITEMS'
FROM im_invoice, im_invoice_line_item,im_invoice_inventory
WHERE invoice_rid = invoice_fk
AND invoice_inventory_rid = invoice_inv_fk
AND enter_date BETWEEN dateadd(mm, -3, getdate()) AND dateadd(mm,0,getdate())
GROUP BY entered_by) AS LineItems
INNER JOIN
(SELECT entered_by, count(invoice_num) 'NO OF INVOICES'
FROM im_invoice
WHERE enter_date BETWEEN dateadd(mm, -3, getdate()) AND dateadd(mm,0,getdate())
GROUP BY entered_by ) AS invoices
ON invoices.entered_by = LineItems.ENTERED_BY

Resources