I am not able to arrive at a reasonable SQL that will return the required id's given the following data set and rules. The id's returned should be 1, 5 and 6.
Rule1 - Return id if both Alcohol and Drug categories are present and Alcohol <= 3 and Drug = No
Rule2 - Return id if only Alcohol category is present and Alcohol <= 3
Rule3 - Return id if only Drug category is present and Drug = No
id Category Value
1 Alcohol 0
1 Drug No
2 Alcohol 5
2 Drug Yes
3 Alcohol 3
3 Drug Yes
4 Alcohol 5
4 Drug No
5 Alcohol 2
6 Drug No
7 Alcohol 5
8 Drug Yes
Thanks in advance.
select
dt.id
from
(
select
id as id
from
#datatbl
group by
id
having
count(*) = 1
) X
inner join #datatbl dt on
dt.id = X.id
where
dt.value in ('0', '1', '2', '3', 'No')
union
select
dt1.id
from
#datatbl dt1
inner join (
select
dt.id
from
(
select
id as id
from
#datatbl
group by
id
having
count(*) = 2
) X
inner join #datatbl dt on
dt.id = X.id
where
dt.category = 'Alcohol'
and
dt.value in ('0', '1', '2', '3')
) X on
X.id = dt1.id
where
dt1.category = 'Drug'
and
dt1.value in ('No')
Rule1:
SELECT *
FROM MyTable t1
INNER JOIN MyTable t2 ON t1.id = t2.id
AND t1.Category <> t2.Category AND t2.Category = 'Drug'
WHERE t1.Category = 'Alcohol' AND t1.Value <= 3 AND t2.Value = 'No'
Rule2:
SELECT *
FROM MyTable t1
LEFT JOIN MyTable t2 ON t1.id = t2.id
AND t1.Category <> t2.Category AND t2.Category = 'Drug'
WHERE t1.Category = 'Alcohol' AND t1.Value <= 3 AND t2.Category IS NULL
Rule3:
SELECT *
FROM MyTable t1
RIGHT JOIN MyTable t2 ON t1.id = t2.id
AND t1.Category <> t2.Category AND t2.Category = 'Drug'
WHERE t2.Value = 'No' AND t1.Category IS NULL
Related
Best to explain with an example :)
Let's say I have a the tables
CREATE TABLE dbo.Customer (
CustomerId INT PRIMARY KEY,
Name NVARCHAR(50)
)
CREATE TABLE dbo.ShoppingBasket (
ShoppingBasketId INT PRIMARY KEY,
CustomerId INT NOT NULL FOREIGN KEY dbo.Customer(CustomerId),
ItemName NVARCHAR(50)
)
Example data
INSERT INTO dbo.Customer
VALUES (1, 'Steve'), (2, 'Bucky')
INSERT INTO dbo.ShoppingBasket
VALUES (1, 1, 'Banana'), (2, 1, 'Orange'), (3, 2, 'Orange')
Now, I want to find all customers, that have a Banana and an Orange in their shopping basket exactly. So in the case above, it should return Steve only. Since Bucky has only a Banana.
The following query works for this
SELECT *
FROM dbo.Customer AS c
WHERE EXISTS (
SELECT 1
FROM dbo.ShoppingBasket AS b
WHERE b.CustomerId = c.CustomerId
AND b.ItemName IN ('Banana', 'Orange')
GROUP BY CustomerId
HAVING COUNT(CustomerId) = 2
)
That's fine. Now, if I want all customers that only have an Orange, the above query fails since
SELECT *
FROM dbo.Customer AS c
WHERE EXISTS (
SELECT 1
FROM dbo.ShoppingBasket AS b
WHERE b.CustomerId = c.CustomerId
AND b.ItemName = 'Orange'
GROUP BY CustomerId
HAVING COUNT(CustomerId) = 1
)
is filtering out the shopping basket and then applying the group and having clause. Thus both Steve and Bucky are return whereas only Bucky should be returned.
Could someone point me in the right direction to find such a query, I suppose I can always do another NOT EXIST inside the exist subquery, to make sure no other items are found. E.g.
SELECT *
FROM dbo.Customer AS c
WHERE EXISTS (
SELECT 1
FROM dbo.ShoppingBasket AS b
WHERE b.CustomerId = c.CustomerId
AND b.ItemName = 'Orange'
AND NOT EXISTS (
SELECT 1
FROM dbo.ShoppingBasket AS b2
WHERE b2.CustomerId = b.CustomerId
AND b.ItemName <> 'Orange'
)
)
But was wandering if there's a more elegant way to handle it. One that preferably doesn't do an extra, negated join on the same table.
you should check the distinct ItemName instead of the customerId eg:
select c.*
from dbo.Customer
inner join(
select CustomerId, count(distinct ItemName) count_name
from ShoppingBasket
where ItemName IN ('Banana', 'Orange')
group by CustomerId
having count_name = 2
) t on t.CustomerId = c.CustomerId
If you need a twice check on number of item name an type you could compose the inner join in two part
select c.*
from dbo.Customer
inner join(
select CustomerId
from ShoppingBasket b
where ItemName IN ('Banana', 'Orange')
INNER JOIN (
Select CustomerId, count(distinct ItemName) count_name
from ShoppingBasket
group by CustomerId
having count_name = 2
) t2 ON t2.CustomerId = b.CustomerId
) t on t.CustomerId = c.CustomerId
and for Orange ..
select c.*
from dbo.Customer
inner join(
select CustomerId
from ShoppingBasket b
where ItemName IN ('Orange')
INNER JOIN (
Select CustomerId, count(distinct ItemName) count_name
from ShoppingBasket
group by CustomerId
having count_name = 1
) t2 ON t2.CustomerId = b.CustomerId
) t on t.CustomerId = c.CustomerId
The problem is that the in clause in ambiguos because return true also for ShoppingBasket CustomerId with One positive check
then instead of an in clause ( equivalent to OR ) you should work on and clause for all the customer that have a number of distinct name equivalent at then number you are looking for
Select CustomerId
from ShoppingBasket a
inner join ShoppingBasket b a.ItemName = 'Orange' and b.ItemName = 'Banana'
and customerId IN (
Select CustomerId
from ShoppingBasket
group by CustomerId
having count(distinct ItemName) = 2
)
I like to do this with group by and having. If you want both "banana" and "orange":
select sb.customerId
from dbo.ShoppingBasket sb
where sb.itemName in ('banana', 'orange')
group by sb.customerId
having count(distinct itemName) = 2; -- has both
If you want the two items and nothing else, then use this more general form:
select sb.customerId
from dbo.ShoppingBasket sb
where sb.itemName in ('banana', 'orange')
group by sb.customerId
having sum(case when sb.itemName = 'banana' then 1 else 0 end) > 0 and
sum(case when sb.itemName = 'orange' then 1 else 0 end) > 0 and
sum(case when sb.itemName not in ('orange, 'banana') then 1 else 0 end) = 0 ;
You can extend this version easily. Each item gets its own sum(). You can also include multiple items to support, say, "banana and (orange or clementine)".
i/p
Id Name InsertBy UpdateBy
1 A 2 2
2 B 1 2
3 C 4 3
4 D 4 5
5 E 1 3
O/P(THE COUNT OF EMPLOYEE ID IN INSERT AND COUNT OF EMPID IN UPDATE)
Name InsertBy UpdateBy
A 2 0
B 1 2
C 0 2
D 2 0
E 0 1
It seems to you would require to do self join with separate query (Inserted, Updated) for safer.
SELECT
t.name, a.insertBy, b.updateBy
FROM table t
INNER JOIN
(
SELECT
t.id, count (t1.insertBy) insertBy
FROM table t
LEFT JOIN table t1 on t1.insertBy = t.id
GROUP BY t.id
)a on a.id = t.id
INNER JOIN
(
SELECT
t.id, count (t2.updateBy) updateBy
FROM table t
LEFT JOIN table t2 on t2.updateBy = t.id
GROUP BY t.id
)b on b.id = t.id
Let me edit with other approach which more efficient with separate join
select t1.name, sum(case when a.Name = 'InsertedBy' then 1 else 0 end) InsertBy,
sum(case when a.Name = 'UpdatedBy' then 1 else 0 end) UpdateBy
from table t
cross apply (
values (InsertBy, 'InsertedBy'), (UpdateBy, 'UpdatedBy')
)a(Types, Name)
LEFT JOIN table t1 on t1.Id = a.Types
group by t1.name
However, these will make use of index on (Id)
I have this query :
select s.LastState
,count(s.LastState) as sumS
from table1 t1
join table2 t2
on t1.ID = t2.ID
join (select LastState
,count(LastState) as sum
from table1
where ID = X
and LastState = 1
or LastState = 2
group by LastState
) s
on s.LastState = t1.LastState
group by s.LastState
This returns the number of both state and I'd like to have the sum of both my counts.
Currently I see my first line with let's admit
10 state 1 and 5 state 2 for my ID X
and I'd like to see 15 (sum of counts for both states).
select --s.LastState
LastState='LastState1and2'
, count(s.LastState) as sumS
from table1 t1
join table2 t2
on t1.ID = t2.ID
join (select LastState
,count(LastState) as sum
from table1
where ID = X
and LastState = 1
or LastState = 2
group by LastState
) s
on s.LastState = t1.LastState
--group by s.LastState
I was hoping someone could help me.
I have the following 5 tables:
tblCustomer
CustomerID CustomerName
------- ------------
1 ABC Bank
2 Chase Bank
tblOrderType
OrderTypeID OrderTypeName
---------- ------------
1 Assignment
2 LienRelease
tblActivity
ActivityID ActivityName
---------- ------------
1 Received
2 Keyed
3 Printed
4 Delivered To Cusotmer
tblOrder
OrderID CustomerID OrderTypeID LoanNumber
---------- ------------ ----------- ----------
1 1 1 45584565
2 1 1 45566856
3 1 1 45565584
4 1 1 45588545
tblOrderActivity
OrderID ActivityID ActivityDate
---------- ----------- ----------
1 1 2007-04-16 8:34:00 AM
1 2 2007-04-16 9:22:00 AM
1 3 2007-04-16 9:51:00 AM
1 4 2007-04-16 4:14:00 PM
2 1 2007-04-16 8:34:00 AM
3 1 2007-04-16 8:34:00 AM
3 2 2007-04-16 9:22:00 AM
3 3 2007-04-16 9:51:00 AM
3 4 2007-04-16 4:14:00 PM
4 1 2007-04-16 8:34:00 AM
4 2 2007-04-16 9:22:00 AM
4 3 2007-04-16 9:51:00 AM
The information has been changed for protection purposes. So say I need to be able to list all Assignment orders that have a “Received” activity but not a “Delivered” activity for ABC Bank and I need to return CustomerName, CustomerID, LoanNumber, and “received date” (Activity Date for received). I have been able to get everything done, however, I am unable to remove any files that have not had an Activity of "Delivered". Here is what I have so far:
SELECT tblCustomer.CustomerName, tblCustomer.CustomerID, tblOrder.LoanNumber, (tblOrderActivity.ActivityDate) AS [received date]
FROM tblOrderActivity (NOLOCK)
INNER JOIN tblOrder (NOLOCK)
ON tblOrderActivity.OrderID = tblOrder.OrderID
INNER JOIN tblCustomer (NOLOCK)
ON tblOrder.CustomerID = tblCustomer.CustomerID
INNER JOIN tblOrderType (NOLOCK)
ON tblOrder.OrderTypeID = tblOrderType.OrderTypeID
INNER JOIN tblActivity (NOLOCK)
ON tblActivity.ActivityID = tblOrderActivity.ActivityID
WHERE tblOrderType.OrderTypeName = 'Assignment'
AND EXISTS (SELECT DISTINCT [OrderID] FROM [tblOrderActivity] WHERE tblActivity.ActivityName = 'Received')
AND NOT EXISTS (SELECT DISTINCT [OrderID] FROM [tblOrderActivity] WHERE tblActivity.ActivityName = 'Delivered To Customer')
GROUP BY tblCustomer.CustomerName, tblCustomer.CustomerID, tblOrder.LoanNumber, tblOrderActivity.ActivityDate;
And my results are below
CustomerName CustomerID LoanNumber received date
---------- ------------ ----------- ----------
ABC Bank 1 45565584 2007-04-16 08:34:00.000
ABC Bank 1 45566856 2007-04-16 08:34:00.000
ABC Bank 1 45584565 2007-04-16 08:34:00.000
ABC Bank 1 45588545 2007-04-16 08:34:00.000
Try this. Not the most efficient t-sql in the world, but the schema doesn't help much. May need to alias the tables in the main query to make it more readable and ensure the subqueries work properly (at least that's what I usually do).
This should return all received records that do not have a delivery
SELECT tblCustomer.CustomerName, tblCustomer.CustomerID, tblOrder.LoanNumber, (tblOrderActivity.ActivityDate) AS [received date]
FROM tblOrderActivity (NOLOCK)
INNER JOIN tblOrder (NOLOCK)
ON tblOrderActivity.OrderID = tblOrder.OrderID
INNER JOIN tblCustomer (NOLOCK)
ON tblOrder.CustomerID = tblCustomer.CustomerID
INNER JOIN tblOrderType (NOLOCK)
ON tblOrder.OrderTypeID = tblOrderType.OrderTypeID
INNER JOIN tblActivity (NOLOCK)
ON tblActivity.ActivityID = tblOrderActivity.ActivityID
WHERE tblOrderType.OrderTypeName = 'Assignment'
AND NOT EXISTS (SELECT 1 FROM [tblOrderActivity] WHERE OA.ActivityID = 4 AND OA.OrderID = tblOrder.OrderID) --Delivered Excluded
AND tblOrderActivity.ActivityID = 1 --Received Only
Here is how I would do the query
SELECT *
FROM tblOrder
LEFT JOIN (
SELECT 'r' AS r, tblOrderActivity.ActivityDate, tblActivity.ActivityName, tblOrderActivity.OrderID
FROM tblOrderActivity
JOIN tblActivity ON tblOrderActivity.ActivityID = tblActivity.ActivityID
AND tblActivity.ActivityName = 'Received'
) AS Received ON tblOrder.OrderID = Received.OrderID
LEFT JOIN (
SELECT 'd' AS d, tblOrderActivity.ActivityDate, tblActivity.ActivityName, tblOrderActivity.OrderID
FROM tblOrderActivity
JOIN tblActivity ON tblOrderActivity.ActivityID = tblActivity.ActivityID
AND tblActivity.ActivityName = 'Delivered To Cusotmer'
) AS Delivered ON tblOrder.OrderID = Delivered.OrderID
WHERE Delivered.ActivityDate IS NULL
or like this
SELECT *
FROM tblOrder
LEFT JOIN (
SELECT 'r' AS r, tblOrderActivity.ActivityDate, tblActivity.ActivityName, tblOrderActivity.OrderID
FROM tblOrderActivity
JOIN tblActivity ON tblOrderActivity.ActivityID = tblActivity.ActivityID
AND tblActivity.ActivityID IN (SELECT ActivityID FROM tblActivity WHERE tblActivity.ActivityName = 'Received')
) AS Received ON tblOrder.OrderID = Received.OrderID
LEFT JOIN (
SELECT 'd' AS d, tblOrderActivity.ActivityDate, tblActivity.ActivityName, tblOrderActivity.OrderID
FROM tblOrderActivity
JOIN tblActivity ON tblOrderActivity.ActivityID = tblActivity.ActivityID
AND tblActivity.ActivityID IN (SELECT ActivityID FROM tblActivity WHERE tblActivity.ActivityName = 'Delivered To Cusotmer')
) AS Delivered ON tblOrder.OrderID = Delivered.OrderID
WHERE Delivered.ActivityDate IS NULL
trim it down to just the columns you want of course
I have 3 tables. They have 1 to many relations
table1 - mainID, Select00(bit), Select01(bit)
table2 - secID, mainID
table3 - secID, Num00, Num01
SELECT table1.mainID , SUM(table3.Num00) as S00, SUM(table3.Num01) as S01
FROM table1 INNER JOIN
table2 ON table1.mainID = table2.mainID INNER JOIN
table3 ON table2.secID = table3.secID
GROUP BY table1.mainID HAVING table1.mainID =11
The following query works, but gives me all sums.
How do I get the SUMs based on condition? i.e
S00 = SUM(table3.Num00) if table1.Select00 = 1 (true)
S01 = SUM(table3.Num01) if table1.Select01 = 1 (true)
SELECT
t1.mainID,
S00 = SUM(CASE WHEN t1.Select00 = 1 THEN t3.Num00 ELSE 0 END),
S01 = SUM(CASE WHEN t1.Select01 = 1 THEN t3.Num01 ELSE 0 END)
FROM
dbo.table1 AS t1
INNER JOIN dbo.table2 AS t2
ON t1.mainID = t2.mainID
INNER JOIN dbo.table3 AS t3
ON t2.secID = t3.secID
WHERE t1.MainID = 11
GROUP BY table1.mainID;