How to use count in subquery in MSSQL - sql-server

I would like to merge two tables in mssql. The first Table have a task column. I would like to count the specific tasks and give the counted result to the second table to the AuftNr.
Here
Do i need a subquery and group by to solve this ?
So far i have done this.
SELECT AB.PersNr as PersonalNumber
,CONVERT(char(10),DATEADD(DAY, AB.Tag, '30.12.1899'),126) AS Day
,CONVERT(char(10),DATEADD(SECOND, AB.Von, DATEADD(DAY, AB.Tag,
'30.12.1899')),108) AS [From]
,AB.Bis as [To]
,AB.Auftrag as Task
FROM AStpVonBis AB
LEFT JOIN Auftrag A ON (A.AuftNr = AB.Auftrag)
INNER JOIN Personen P ON (P.PersNr = AB.PersNr)
WHERE P.Abteilung = 170 AND AB.Tag = DATEDIFF(DAY, '30.12.1899', GETDATE())
AND AB.Bis = -2
SELECT A.AuftNr FROM Auftrag A

Using a GROUP BY and a COUNT should do it :
SELECT
AB.Auftrag as Task,
count(*) as Total
FROM AStpVonBis AB
JOIN Personen P ON (P.PersNr = AB.PersNr)
WHERE P.Abteilung = 170
AND AB.Tag = DATEDIFF(DAY, convert(date,'30.12.1899',104), GETDATE())
AND AB.Bis = -2
GROUP BY AB.Auftrag
ORDER BY AB.Auftrag
Note that the left join with [Auftrag] wasn't included.
Since there's already AB.Auftrag to group by, and there's no grouping needed on the name of the Task.
The date stamp is converted with the 104 format to a date.
Just so it'll also work on connections that use another default date format.
Disclaimer: only tested in notepad

If I understand your question correctly, the bellow query should work
DECLARE #Count TABLE (PhoneNumver INT, [Day] DATE,[From] VARCHAR(150),[To] VARCHAR(3),Task INT)
INSERT INTO #Count
VALUES(1003,'2017-06-28','07:46:20','-2',150 ),
(1010,'2017-06-28','11:44:47','-2',140),
(1012,'2017-06-28','10:57:00','-2',120 ),
(1016,'2017-06-28','12:20:16','-2',120 ),
(1019,'2017-06-28','08:31:03','-2',120 ),
(1020,'2017-06-28','11:38:02','-2',120 ),
(1021,'2017-06-28','07:54:55','-2',120 ),
(1025,'2017-06-28','11:38:12','-2',120 ),
(1027,'2017-06-28','09:47:46','-2',130 )
DECLARE #Task TABLE (AuftNr INT)
INSERT INTO #Task VALUES (110),(120),(130),(140),(150),(200),(210),(220),(230)
SELECT
A.AuftNr,
COUNT(C.Task) AS Total_Count
FROM #Task A
LEFT JOIN #Count C ON A.AuftNr=C.Task
--From here you can add all the exclussions in where clause
GROUP BY A.AuftNr
ORDER BY Total_Count DESC
OUTPUT
AuftNr Total_Count
120 6
130 1
140 1
150 1
200 0
210 0
220 0
230 0
110 0

Related

SQL - Finding Gaps in Coverage

I am running this problem on SQL server
Here is my problem.
have something like this
Dataset A
FK_ID StartDate EndDate Type
1 10/1/2018 11/30/2018 M
1 12/1/2018 2/28/2019 N
1 3/1/2019 10/31/2019 M
I have a second data source I have no control over with data something like this:
Dataset B
FK_ID SpanStart SpanEnd Type
1 10/1/2018 10/15/2018 M
1 10/1/2018 10/25/2018 M
1 2/15/2019 4/30/2019 M
1 5/1/2019 10/31/2019 M
What I am trying to accomplish is to check to make sure every date within each TYPE M record in Dataset A has at least 1 record in Dataset B.
For example record 1 in Dataset A does NOT have coverage from 10/26/2018 through 11/30/2018. I really only care about when the coverage ends, in this case I want to return 10/26/2018 because it is the first date where the span has no coverage from Dataset B.
I've written a function that does this but it is pretty slow because it is cycling through each date within each M record and counting the number of records in Dataset B. It exits the loop when it finds the first one but I would really like to make this more efficient. I am sure I am not thinking about this properly so any suggestions anyone can offer would be helpful.
This is the section of code I'm currently running
else if #SpanType = 'M'
begin
set #CurrDate = #SpanStart
set #UncovDays = 0
while #CurrDate <= #SpanEnd
Begin
if (SELECT count(*)
FROM eligiblecoverage ec join eligibilityplan ep on ec.plandescription = ep.planname
WHERE ec.masterindividualid = #IndID
and ec.planbegindate <= #CurrDate and ec.planenddate >= #CurrDate
and ec.sourcecreateddate = #MaxDate
and ep.medicaidcoverage = 1) = 0
begin
SET #Result = concat('NON Starting ',format(#currdate, 'M/d/yyyy'))
BREAK
end
set #CurrDate = #CurrDate + 1
end
end
I am not married to having a function it just could not find a way to do this in queries that wasn't very very slow.
EDIT: Dataset B will never have any TYPEs except M so that is not a consideration
EDIT 2: The code offered by DonPablo does de-overlap the data but only in cases where there is an overlap at all. It reduces dataset B to:
FK_ID SpanStart SpanEnd Type
1 10/1/2018 10/25/2018 M
instead of
FK_ID SpanStart SpanEnd Type
1 10/1/2018 10/25/2018 M
1 2/15/2019 4/30/2019 M
1 5/1/2019 10/31/2019 M
I am still futzing around with it but it's a start.
I would approach this by focusing on B. My assumption is that any absent record would follow span_end in the table. So here is the idea:
Unpivot the dates in B (adding "1" to the end dates)
Add a flag if they are present with type "M".
Check to see if any not-present records are in the span for A.
Check the first and last dates as well.
So, this looks like:
with bdates as (
select v.dte,
(case when exists (select 1
from b b2
where v.dte between b2.spanstart and b2.spanend and
b2.type = 'M'
)
then 1 else 0
end) as in_b
from b cross apply
(values (spanstart), (dateadd(day, 1, spanend)
) v(dte)
where b.type = 'M' -- all we care about
group by v.dte -- no need for duplicates
)
select a.*,
(case when not exists (select 1
from b b2
where a.startdate between b2.spanstart and b2.spanend and
b2.type = 'M'
)
then 0
when not exists (select 1
from b b2
where a.enddate between b2.spanstart and b2.spanend and
b2.type = 'M'
)
when exists (select 1
from bdates bd
where bd.dte between a.startdate and a.enddate and
bd.in_b = 0
)
then 0
when exists (select 1
from b b2
where a.startdate between b2.spanstart and b2.spanend and
b2.type = 'M'
)
then 1
else 0
end)
from a;
What is this doing? Four validity checks:
Is the starttime valid?
Is the endtime valid?
Are any intermediate dates invalid?
Is there at least one valid record?
Start by framing the problem in smaller pieces, in a sequence of actions like I did in the comment.
See George Polya "How To Solve It" 1945
Then Google is your friend -- look at==> sql de-overlap date ranges into one record (over a million results)
UPDATED--I picked Merge overlapping dates in SQL Server
and updated it for our table and column names.
Also look at theory from 1983 Allen's Interval Algebra https://www.ics.uci.edu/~alspaugh/cls/shr/allen.html
Or from 2014 https://stewashton.wordpress.com/2014/03/11/sql-for-date-ranges-gaps-and-overlaps/
This is a primer on how to setup test data for this problem.
Finally determine what counts via Ranking the various pairs of A vs B --
bypass those totally Within, then work with earliest PartialOverlaps, lastly do the Precede/Follow items.
--from Merge overlapping dates in SQL Server
with SpanStarts as
(
select distinct FK_ID, SpanStart
from Coverage_B as t1
where not exists
(select * from Coverage_B as t2
where t2.FK_ID = t1.FK_ID
and t2.SpanStart < t1.SpanStart
and t2.SpanEnd >= t1.SpanStart)
),
SpanEnds as
(
select distinct FK_ID, SpanEnd
from Coverage_B as t1
where not exists
(select * from Coverage_B as t2
where t2.FK_ID = t1.FK_ID
and t2.SpanEnd > t1.SpanEnd
and t2.SpanStart <= t1.SpanEnd)
),
DeOverlapped_B as
(
Select FK_ID, SpanStart,
(select min(SpanEnd) from SpanEnds as e
where e.FK_ID = s.FK_ID
and SpanEnd >= SpanStart) as SpanEnd
from SpanStarts as s
)
Select * from DeOverlapped_B
Now we have something to feed into the next steps, and we can use the above as a CTE
======================================
with SpanStarts as
(
select distinct FK_ID, SpanStart
from Coverage_B as t1
where not exists
(select * from Coverage_B as t2
where t2.FK_ID = t1.FK_ID
and t2.SpanStart < t1.SpanStart
and t2.SpanEnd >= t1.SpanStart)
),
SpanEnds as
(
select distinct FK_ID, SpanEnd
from Coverage_B as t1
where not exists
(select * from Coverage_B as t2
where t2.FK_ID = t1.FK_ID
and t2.SpanEnd > t1.SpanEnd
and t2.SpanStart <= t1.SpanEnd)
),
DeOverlapped_B as
(
Select FK_ID, SpanStart,
(select min(SpanEnd) from SpanEnds as e
where e.FK_ID = s.FK_ID
and SpanEnd >= SpanStart) as SpanEnd
from SpanStarts as s
),
-- find A row's coverage
ACoverage as (
Select
a.*, b.SpanEnd, b.SpanStart,
Case
When SpanStart <= StartDate And StartDate <= SpanEnd
And SpanStart <= EndDate And EndDate <= SpanEnd
Then '1within' -- starts, equals, during, finishes
When EndDate < SpanStart
Or SpanEnd < StartDate
Then '3beforeAfter' -- preceeds, meets, preceeded, met
Else '2overlap' -- one or two ends hang over spanStart/End
End as relation
From Coverage_A a
Left Join DeOverlapped_B b
On a.FK_ID = b.FK_ID
Where a.Type = 'M'
)
Select
*
,Case
When relation1 = '2' And StartDate < SpanStart Then StartDate
When relation1 = '2' Then DateAdd(d, 1, SpanEnd)
When relation1 = '3' Then StartDate
End as UnCoveredBeginning
From (
Select
*
,SUBSTRING(relation,1,1) as relation1
,ROW_NUMBER() Over (Partition by A_ID Order by relation, SpanStart) as Rownum
from ACoverage
) aRNO
Where Rownum = 1
And relation1 <> '1'

Second level lookup with SQL statement

How do I write a SQL statement that does a second level lookup only if first is not matched. For example:
In the below query, if my SEDOLCode condition does not return a record, proceed to lookup with condition 2 with RICCode.
select
*, GETDATE()
from
Securities sec
where
sec.SEDOLCode = 'ABCDEF'
or sec.RICCode = '002815.SZ'
This query is returning two different records - for example:
1234 ABCDEF DUMY906.X
5675 EFTFS 002815.SZ
I am taking data from a file to update the Pricetable as below. I want to use SedolCode as primary lookup.
IF ##ROWCOUNT = 0
INSERT INTO dbo.Price (sec.SecurityID, ClosingPrice, UpdatedDate, UpdatedByUser, Priced)
SELECT
..., GETDATE()
FROM
Securities sec
WHERE
sec.SEDOLCode = #SedolCode
OR sec.RICCode = #RicCode
Try this the logic is basically if the sedolcode is found then it will only meet the first condition. Otherwise the count of that sedolcolde will be 0 and it will look at riccode.
select
*, GETDATE()
from
Securities sec
where
sec.sedolcode = 'ABCDEF'
OR ((SELECT COUNT(1) FROM securites WHERE sedolcode ='ABCDEF') = 0 AND sec.riccode = '002815.SZ')
Ah - reminds me of my FTSE days........
Match Sedol and Not Ric
or
Ric and Not Sedol and use myOrdering & TOP to get the first.
INSERT INTO dbo.Price
(
sec.SecurityID
, ClosingPrice
, UpdatedDate
, UpdatedByUser
, Priced
)
SELECT TOP 1 [specifiy fields to insert]
FROM
(
select 1 as myOrdering ...
, GETDATE()
from Securities sec
WHERE
(sec.RICCode = #RicCode AND sec.SEDOLCode != #SedolCode)
UNION
select 2 as myOrdering ...
, GETDATE()
from Securities sec
WHERE
(sec.RICCode = #RicCode AND sec.SEDOLCode != #SedolCode)
)SUB_Q ORDER BY myOrdering

SSRS:How to return count of events per day for Month

I have a table with the following information
ID,DateTime,EventType
1,6/5/2013 9:35:00,B
1,6/5/2013 9:35:24,A
2,6/5/2013 9:35:36,B
3,6/5/2013 9:36:11,D
2,6/5/2013 9:39:16,A
3,6/5/2013 9:40:48,B
4,7/5/2013 9:35:19,B
4,7/5/2013 9:35:33,A
5,7/5/2013 9:35:53,B
5,7/5/2013 9:36:06,D
6,7/5/2013 9:39:39,A
7,7/5/2013 9:40:28,B
8,8/5/2013 9:35:02,A
7,8/5/2013 9:35:08,A
8,8/5/2013 9:35:29,B
6,8/5/2013 9:36:39,B
I need to count how many times each day an event changed state as long as the time between states was less than 30 seconds over the time period.
Basically I am looking for the following result set
6/5/2013 | 1
7/5/2013 | 2
8/5/2013 | 1
I've tried several different types of queries, but nothing works. I am using SQL Server Reporting Services 2008.
declare #t table (ID int,[DateTime] datetime ,EventType varchar);
insert #t values
(1,'6/5/2013 9:35:00','B'),
(1,'6/5/2013 9:35:24','A'),
(2,'6/5/2013 9:35:36','B'),
(3,'6/5/2013 9:36:11','D'),
(2,'6/5/2013 9:39:16','A'),
(3,'6/5/2013 9:40:48','B'),
(4,'7/5/2013 9:35:19','B'),
(4,'7/5/2013 9:35:33','A'),
(5,'7/5/2013 9:35:53','B'),
(5,'7/5/2013 9:36:06','D'),
(6,'7/5/2013 9:39:39','A'),
(7,'7/5/2013 9:40:28','B'),
(8,'8/5/2013 9:35:02','A'),
(7,'8/5/2013 9:35:08','A'),
(8,'8/5/2013 9:35:29','B'),
(6,'8/5/2013 9:36:39','B');
--select * from #t order by ID, DateTime;
with cte as (
select *, cast([DateTime] as date) the_date, row_number() over (partition by ID order by DateTime) row_num
from #t
)
select c1.the_date, count(1)
from cte c1
join cte c2
on c2.ID = c1.ID
and c2.row_num = c1.row_num + 1
where datediff(S,c1.DateTime, c2.DateTime) < 30
group by c1.the_date
order by c1.the_date;
Try this:
select CONVERT(VARCHAR(10), a.DateTime, 103) [Date], count(a.ID) Count from Table a
inner join Table b on a.ID = b.ID
where DATEDIFF(second,a.DateTime,b.DateTime) between 1 and 29 and a.ID = b.ID
and Cast(a.DateTime as Date) = Cast(b.DateTime as date)
group by CONVERT(VARCHAR(10), a.DateTime, 103)

SQL query to show good records as well as null records

My query works perfectly well to find records with real values, however, I also need my query to show records with null values. So far my attempts at recreating this query to also show null values has resulted in losing at least 1 of my columns of results so now I'm looking for help.
This is my query so far:
SELECT sq.*, sq.TransactionCountTotal - sq.CompleteTotal as InProcTotal from
(
select
c.CustName,
t.[City],
sum (t.TransactionCount) as TransactionCountTotal
sum (
case
when (
[format] in (23,25,38)
or [format] between 400 and 499
or format between 800 and 899
)
then t.TransactionCount
else 0
end
) as CompleteTotal
FROM [log].[dbo].[TransactionSummary] t
INNER JOIN [log].[dbo].[Customer] c
on t.CustNo = c.CustNo
and t.City = c.City
and t.subno = c.subno
where t.transactiondate between '7/1/16' and '7/11/16'
group by c.CustName,t.City
) sq
This is currently what my query results show:
CustName City InProcTotal TransactionCountTotal Complete Total
Cust 1 City(a) 23 7 30
Cust 2 City(b) 74 2 76
Cust 3 City(c) 54 4 58
This is what I want my query results to show:
CustName City InProcTotal TransactionCountTotal Complete Total
Cust 1 City(a) 23 7 30
Cust 2 City(b) 74 2 76
Cust 3 City(c) 54 4 58
Cust 4 City(d) 0 0 0
Cust 5 City(e) 0 0 0
I suggest you use RIGHT JOIN in the place of INNER JOIN. You should then retain the rows from Customer that don't have matching rows in TransactionSummary.
You may also want to refactor the query like this so you use LEFT JOIN. The next person to work on the query will thank you; LEFT JOIN operations are more common.
FROM [log].[dbo].[Customer] c
LEFT JOIN [log].[dbo].[TransactionSummary] t
on t.CustNo = c.CustNo
and t.City = c.City
jwabsolution, your issue stems from grabbing all transactions instead of all customers. My mind works in this way: you want to select all of the customers & find all transaction states. Therefore, you should be selecting from the customer table. Also, you shouldn't use the INNER JOIN or you will ignore any customers that don't have transactions. Instead, use left join the transactions table. In this manner, you will retrieve all customers (even those with no transactions). Here is a good visual for SQL joins: http://www.codeproject.com/KB/database/Visual_SQL_Joins/Visual_SQL_JOINS_orig.jpg
So your query should look like this:
SELECT sq.*, sq.TransactionCountTotal - sq.CompleteTotal as InProcTotal from
(
select
c.CustName,
t.[City],
sum (t.TransactionCount) as TransactionCountTotal
sum (
case
when (
[format] in (23,25,38)
or [format] between 400 and 499
or format between 800 and 899
)
then t.TransactionCount
else 0
end
) as CompleteTotal
FROM [log].[dbo].[Customer] c
LEFT JOIN [log].[dbo].[TransactionSummary] t
on c.CustNo = t.CustNo
and c.City = t.City
and c.subno = t.subno
where t.transactiondate between '7/1/16' and '7/11/16'
group by c.CustName,t.City
) sq
Fixed it. Needed to use coalesce to get the values to show up properly.
Also added a "where" option if I want to query individual customers
SELECT sq.* ,sq.TransactionCountTotal - sq.CompleteTotal as [InProcTotal]
from
(
select
c.custname
,c.port
,sum(coalesce(t.transactioncount,0)) as TransactionCountTotal
,sum(
case when (
[format]in(23,25,38)
or[format]between 400 and 499
or[format]between 800 and 899)
then t.TransactionCount
else 0
end) as CompleteTotal
from log.dbo.customer c
left join log.dbo.TransactionSummary t
on c.custNo=t.custno
and c.subno=t.subno
and c.city=t.city
and t.transactiondate between '7/1/16' and '7/12/16'
/*where c.custname=''*/
group by c.custname,c.city
) sq

T-SQL: AGGREGATE AND/OR CONDITIONAL SUBQUERY ON WHERE CLAUSE

Thanks in advance for your help on this. Here's the the scenario. I have two tables: Lots (stores lot info for food items) and Trans (stores inventory transactions on lot items).
I am trying to write a query that lists all the transactions that are older than 90 days based 3 conditions on the where clause somehow:
CurrentQty (on Lots table) > 0
If TransactionType = Shipment AND TransactionDate > 90 days (from current date) OR..
IF TransactionType = Receipt AND TransactionDate > 90 days (from current date)
Notes: for each lot, there could be many different transactions of the same type, as shown in the attached picture. There could be many shipments or receipts. I need to be able to select the MAX(TransactionDate) for a particular transaction type and check to see if it's over 90 days then show the record.
There will always be at least one of these two transaction types on the transaction table for every lot, either Shipment or Receipt. If there is no shipment transaction type on a particular lot, then I want to use the Max(TransactionDate) > 90 condition for the "receipt" transaction type.
I need to be able to evaluate all these conditions for each lot and it's particular transactions.
Below is the query I started to write, but then got stuck on how to structure the rest.
SELECT
LOTS.LOTNUMBER, TRANS.ITEMNUMBER, TRANS.DESCRIPTION,
TRANS.TRANSACTIONTYPE, TRANS.TRANSACTIONDATE, TRANS.WAREHOUSE,
TRANS.QUANTITY
FROM
LOTS
INNER JOIN
TRANS ON LOTS.LOTNUMBER = TRANS.LOTNUMBER
WHERE
LOTS.CURRENTQUANTITY > 0
SELECT
LOTS.LOTNUMBER, TRANS.ITEMNUMBER, TRANS.DESCRIPTION,
TRANS.TRANSACTIONTYPE, TRANS.TRANSACTIONDATE, TRANS.WAREHOUSE,
TRANS.QUANTITY
FROM
LOTS
INNER JOIN
(Select LotNumber, Max(TransactionDate) MaxTransDate From TRANS Group By LotNumber) Trans ON LOTS.LOTNUMBER = TRANS.LOTNUMBER
WHERE
LOTS.CURRENTQUANTITY > 0 And
DATEDIFF(d, TRANS.TRANSACTIONDATE, getdate()) > 90 And
(TRANS.TRANSACTIONTYPE = 'Reciept' Or
TRANS.TRANSACTIONTYPE = 'Shipment')
DECLARE #lots TABLE (
LotNumber NVARCHAR(10)
,ItemNum NVARCHAR(12)
,CurrentQty INT
)
DECLARE #trans TABLE (
TransNum INT
,ItemNum NVARCHAR(12)
,Description NVARCHAR(30)
,TransType NVARCHAR(10)
,TransDate DATE
,Warehouse NVARCHAR(5)
,Quantity INT
)
INSERT INTO #lots VALUES
('ABC','10-MAND-5879',925)
INSERT INTO #trans VALUES
(398741,'10-MAND-5879','10 Lb Mandarin Bag','Receipt','2016-01-01','IXCST',100)
,(973541,'10-MAND-5879','10 Lb Mandarin Bag','Shipment','2016-02-04','WTGS',365)
,(972547,'10-MAND-5879','10 Lb Mandarin Bag','Receipt','2016-02-29','GKWK',250)
,(632403,'10-MAND-5879','10 Lb Mandarin Bag','Shipment','2016-03-01','GWSJ',150)
,(692147,'10-MAND-5879','10 Lb Mandarin Bag','Shipment','2016-03-17','ABTK',25)
;WITH MaxShip
AS (
SELECT ItemNum, TransNum,
ROW_NUMBER () OVER (PARTITION BY ItemNum ORDER BY TransDate DESC) AS TransOrder
FROM #trans
WHERE TransType = 'Shipment'
),
MaxRcpt
AS (
SELECT ItemNum, TransNum,
ROW_NUMBER () OVER (PARTITION BY ItemNum ORDER BY TransDate DESC) AS TransOrder
FROM #trans
WHERE transtype = 'Receipt'
)
SELECT *
FROM #trans t
LEFT JOIN #lots l
ON t.ItemNum = l.ItemNum
JOIN MaxShip ms
ON ms.TransNum = t.TransNum
WHERE TransOrder = 1 AND CurrentQty > 0 AND DATEADD(dd,90,TransDate) < GETDATE()
UNION
SELECT *
FROM #trans t
LEFT JOIN #lots l
ON t.ItemNum = l.ItemNum
JOIN MaxRcpt mr
ON mr.TransNum = t.TransNum
WHERE TransOrder = 1 AND CurrentQty > 0 AND DATEADD(dd,90,TransDate) < GETDATE()

Resources