Use NOT Equal condition in sql? - sql-server

I want to fetch orders that have a “Received” (ActivityID = 1) activity but not a “Delivered” (ActivityID = 4) activity on orders table. i.e orders that are received but not deliverd yet.
my query is
SELECT OrderID FROM tblOrderActivity
where (tblOrderActivity.ActivityID = 1 AND tblOrderActivity.ActivityID != 4)
GROUP BY OrderID
it is not returning desired result.
result should be orderID 2 and 4

Your query doesn't really make sense. Grouping happens after WHERE clause, so you're basically getting all orders that have ActivityID ==1 (because if activity Id is 1 there it's always not equal to 4).
After WHERE clause is applied you end up with following rows:
OrderID ActivityID
1 1
2 1
3 1
4 1
And these are the orders you group. No more condition is evaluated.
If 4 is the highest possible ActivityID you could do following:
SELECT OrderID
FROM tblOrderActivity
GROUP BY OrderID
HAVING MAX(ActivityID) < 4
HAVING condition is applied after grouping, which is what you want.

I don't think Group by is needed here. You can use a Subquery to find he order's which is not delivered. Try this.
SELECT *
FROM Yourtable a
WHERE a.ActivityID = 1
AND NOT EXISTS (SELECT 1
FROM yourtable b
WHERE a.OrderID = b.OrderID
AND b.ActivityID = 4)

Related

Count number of rows between start and end date

I have a table with few hundred thousand rows, with columns containing a start and finish datetime, something like this:
ID StartDateTime FinishDateTime
--------------------------------------------------------
1 2001-01-01 04:05:06.789 2001-02-03 04:05:06.789
2 2001-01-01 05:05:06.789 2001-01-01 07:05:06.789
3 2001-01-01 06:05:06.789 2001-02-04 07:05:06.789
4 2001-03-01 06:05:06.789 2001-02-03 04:05:06.789
For each row, I need to count the number of 'active' rows at the start time; as in count rows that start before and finish after the startdatetime for each row. For instance: for ID=3, the startdatetime falls between the startdatetime and finishdatetime of ID=1 and ID=2, but not ID=3 or ID=4, so it should return 2.
The desired output is:
ID ActiveRows
-----------------
1 0
2 1
3 2
4 0
I can get it to work using the query below, but it takes hours to run.
select
ID,
(select count(1)
from table tbl2
where tbl2.StartDateTime < tbl.StartDateTime
and tbl2.FinishDateTime > tbl.StartDateTime) as 'ActiveRows'
from
table tbl
I've also tried joining the table on itself, but it also seems extremely slow.
select
tbl.ID, count(1)
from
table tbl
left join table
tbl2 on tbl2.StartDateTime < tbl.StartDateTime
and tbl2.FinishDateTime > tbl.StartDateTime
group by
tbl.ID
What is the fastest way to perform this calculation?
You can do this using Apply operator
SELECT tbl.id,
oa.activerows
FROM yourtable tbl
OUTER apply(SELECT Count(tbl2.id)
FROM yourtable tbl2
WHERE tbl2.startdatetime < tbl.startdatetime
AND tbl2.finishdatetime > tbl.startdatetime) oa (activerows)
and your original query should be using LEFT JOIN to get the ID's with 0 count
To further improve the performance you can create a non clustered index on yourtable
Create Nonclustered Index Nix_table on
yourtable (startdatetime,finishdatetime) Include (Id)
Live Demo

T-SQL "<> ANY(subquery)"

I have a question about the Any-Operator.
On Technet it says
For example, the following query finds customers located in a territory not covered by any sales persons.
Use AdventureWorks2008R2;
GO
SELECT
CustomerID
FROM
Sales.Customer
WHERE
TerritoryID <> ANY
(
SELECT
TerritoryID
FROM
Sales.SalesPerson
);
Further
The results include all customers, except those whose sales territories are NULL, because every territory that is assigned to a customer is covered by a sales person. The inner query finds all the sales territories covered by sales persons, and then, for each territory, the outer query finds the customers who are not in one.
But that query returns all customers.
I updated a customers TerritoryID to a value that no sales.person has, but still that query returns all customers, instead of that one I expected ..
Am I missing something ?
Might it be that that article on technet is simply wrong ?
https://technet.microsoft.com/de-de/library/ms187074(v=sql.105).aspx (german)
There is one customer with TerritoryID = 13
Inner query result (SELECT TerritoryID FROM Sales.SalesPerson) :
4
2
4
3
6
5
1
4
6
1
1
6
9
1
8
10
7
And in table Sales.Customer is a row with CustomerID = 13, which is the one not covered by a sales-person..
create table #t1
(
id int
)
insert into #t1
values(1),(2),(3)
As you can see,T1 has three values
now lets see,how Any Works
When 'is Equal to ' is used with any ,it works like IN
select * from #t1 where id=
any(select 0)--no result
when Any is used with > or <> ,Any means get me all the values which are greater than minimum value
select * from #t1 where id<>
any(select 1)--2,3
select * from #t1 where id<>
any(select 0)--1,2,3
If your subquery returns one value,the outer query will try to get values which are greater than inner query
<> ANY means any Sales.Customer with a TerritoryID that is Greater Than or Less Than any of the TerritoryID's in the Sales.SalesPerson
so TerritoryID = 13 is greater than all or your examples (4 2 4 3 6 5 1 4 6 1 1 6 9 1 8 10 7), so it's included.
<> ALL is the equivalent of NOT IN so that is what you're confusing <> ANY with
Look at <> ANY as, if there are any records in the set that are not equal to the quailifier, then include it.
The following query has the same result:
SELECT CustomerID FROM Sales.Customer
WHERE TerritoryID NOT IN (SELECT TerritoryID FROM Sales.SalesPerson)

unique chat records sql

I have DB which having 5 column as follows:
message_id
user_id_send
user_id_rec
message_date
message_details
Looking for a SQL Serve Query, I want to Filter Results from two columns (user_id_send,user_id_rec)for Given User ID based on following constrains:
Get the Latest Record (filtered on date or message_id)
Only Unique Records (1 - 2 , 2 - 1 are same so only one record will be returned which ever is the latest one)
Ordered by Descending based on message_id
SQL Query
The main purpose of this query is to get records of user_id to find out to whom he has sent messages and from whom he had received messages.
I have also attached the sheet for your reference.
Here is my try
WITH t
AS (SELECT *
FROM messages
WHERE user_id_sender = 1)
SELECT DISTINCT user_id_reciever,
*
FROM t;
WITH h
AS (SELECT *
FROM messages
WHERE user_id_reciever = 1)
SELECT DISTINCT user_id_sender,
*
FROM h;
;WITH tmpMsg AS (
SELECT M2.message_id
,M2.user_id_receiver
,M2.user_id_sender
,M2.message_date
,M2.message_details
,ROW_NUMBER() OVER (PARTITION BY user_id_receiver+user_id_sender ORDER BY message_date DESC) AS 'RowNum'
FROM messages M2
WHERE M2.user_id_receiver = 1
OR M2.user_id_sender = 1
)
SELECT T.message_id
,T.user_id_receiver
,T.user_id_sender
,T.message_date
,T.message_details
FROM tmpMsg T
WHERE RowNum <= 1
The above should fetch you the results you are looking for when you query for a particular user_id (replace the 1 with parameter e.g. #p_user_id). The user_id_receiver+user_id_sender in the PARTITION clause ensure that records with user id combinations such as 1 - 2, 2 - 1 are not selected twice.
Hope this helps.
select * from
(
select ROW_NUMBER() over (order by message_date DESC) as rowno,
* from messages
where user_id_receiver = 1
--order by message_date DESC
) T where T.rowno = 1
UNION ALL
select * from
(
select ROW_NUMBER() over (order by message_date DESC) as rowno,
* from messages
where user_id_sender = 1
-- order by message_date DESC
) T where T.rowno = 1
Explanation: For each group of user_id_sender, it orders internally by message_date desc, and then adds row numbers, and we only want the first one (chronologically last). Then do the same for user_id_receiver, and union the results together to get 1 result set with all the desired rows. You can then add your own order by clause and additional where conditions at the end as required.
Of course, this only works for any 1 user_id at a time (replace =1 with #user_id).
To get a result from all user_id's at once, is a totally different query, so I hope this helps?

TSQL count rows for group by

I need the count of rows after the group by.
SELECT COUNT(CheckNumber)
FROM myTable
WHERE Status = 'Good'
GROUP BY CheckNumber
Results:
Row 1 = 1
Row 2 = 15
Row 3 = 5
I also tried using DISTINCT
SELECT COUNT(DISTINCT CheckNumber)
FROM myTable
WHERE Status = 'Good'
GROUP BY CheckNumber
Results:
Row 1 = 1
Row 2 = 1
Row 3 = 1
I want the results to be 3
It seems to me the GROUP BY is entirely redundant based on what you say you want. Why not just:
SELECT COUNT(distinct CheckNumber)
FROM myTable
WHERE Status = 'Good'
If I understand correctly you could do this:
SELECT COUNT(*) FROM
(
SELECT COUNT(CheckNumber)
FROM myTable
WHERE Status = 'Good'
GROUP BY CheckNumber
) as myData
This should give you your total of 3 as requested.

Return id where count of value associated with that id on other table is 0

I have been stuck at a fairly simple scenario but even after scratching my head around for sometime I haven't been able to find a solution... Here's what I have.
I have got 2 tables with following data:
Trip (ID, Status)
30063 SUBMITTED
30066 SUBMITTED
30067 ASSIGNED
30068 SUBMITTED
And
AgentTripAssignment(TripId, AgentId, IsRejected)
30063 5 1
30063 2 0
30066 3 0
30066 4 0
30067 1 0
30067 2 0
30067 3 0
What I want to do is:
Return the trip id from trip table where status is SUBMITTED and if entry for trip is present in other table, count of IsRejected = 1 is zero in the AgentTripAssignment table with 1 query (as new enquiries....30066,30068 in the given case) and
Return the trip id from trip table where status is SUBMITTED and entry is present in AgentTripAssignment table having count of IsRejected = 1 appearing at least once in the table with second query (as agent rejected....30063 in the given case)
Other point of note is that the status of trip stays SUBMITTED until 3 agents are not assigned to a trip request which is when the status changes to ASSIGNED as is the case with 30067.
Any help will be much appreciated!
Case 1: Trip Submitted, No 'Rejected' Assignments
This is using a left join on assignments to handle the case of no assignments for a given trip. If the count of assignments in rejected state is not 0, it's excluded.
SELECT t.ID
FROM Trip AS t
LEFT JOIN AgentTripAssignmentx AS at
ON at.TripId = t.ID
WHERE t.Status = 'SUBMITTED'
GROUP BY t.ID
HAVING COUNT(CASE WHEN at.IsRejected = 1 THEN 1 ELSE NULL END) = 0
Case 2: Trip Submitted, Has 'Rejected' Assignments
Similar to above, but using an inner join, and excluding trips that don't have assignments rejected.
SELECT t.ID
FROM Trip AS t
JOIN AgentTripAssignmentx AS at
ON at.TripId = t.ID
WHERE t.Status = 'SUBMITTED'
GROUP BY t.ID
HAVING COUNT(CASE WHEN at.IsRejected = 1 THEN 1 ELSE NULL END) > 0
select a.tripid
from agenttripassignment a
join trip b on a.tripid = b.tripid
where b.status = 'submitted'
group by a.tripid
having max(a.isrejected) = 0
and likewise with 1
This is an example (in SQL Server syntax) that satisfies your first bullet point.
I broke the query down to try to explain the logic. (first 2 table statements are your data table samples)
WITH T AS ( -- Sample data
SELECT 30063 AS ID, 'Submitted' as Status
UNION ALL SELECT 30066, 'Submitted'
UNION ALL SELECT 30067, 'Assigned'
UNION ALL SELECT 30068, 'Submitted'
), A AS ( -- Sample data
SELECT 30063 AS ID, 5 AS AgentID, 1 AS IsRejected
UNION ALL SELECT 30063, 2, 0
UNION ALL SELECT 30066, 3, 0
UNION ALL SELECT 30066, 4, 0
UNION ALL SELECT 30067, 1, 0
UNION ALL SELECT 30067, 2, 0
UNION ALL SELECT 30067, 3, 0
), TPres AS ( -- get list of all IDs present in A table.
SELECT DISTINCT A.ID
FROM A
), RejCnt AS ( -- get List of all items with a rejected status
SELECT DISTINCT A.ID
FROM A
WHERE IsRejected=1
)
SELECT T.ID
FROM T
JOIN TPres ON TPres.ID=T.ID -- Filter out where entry for trip is in the other table.
LEFT JOIN RejCnt ON RejCnt.ID=T.ID -- Used to Determine if IsRejected is Zero
WHERE T.Status='Submitted' -- Where status=Submitted
AND RejCnt.ID IS NULL -- Has no rejected entries
This is very simply done with semi-joins (joins that filter rows but do not provide access to data and do not cause duplication of rows). For your first request, here is an anti-semi-join:
SELECT T.*
FROM
dbo.Trip T
WHERE
T.Status = 'SUBMITTED'
AND NOT EXISTS (
SELECT *
FROM dbo.AgentTripAssignment AT
WHERE
T.ID = AT.TripId
AND AT.IsRejected = 1
)
;
See a Live Demo at SQL Fiddle
This is very simply read as, "show me all rows from table Trip where no matching row (based on ID) exists in table AgentTripAssignment that has IsRejected = 1".
For your second query, it is almost exactly the same, simply change AND NOT EXISTS to EXISTS: "show all rows from table Trip where at least one matching row (based on ID) exists in table AgentTripAssignment that has IsRejected = 1".
The reason I prefer the EXISTS semi-join syntax is that it can often be better performance, depending on the exact indexes. It also helps database developers think in ways that I think are beneficial. It also has the benefit of not having to tack on a DISTINCT to fix duplication, and it avoids the problems of an aggregate method by allowing you to return all rows from the desired table (when a GROUP BY would have to contain all the columns in the table).

Resources