I have a table with column called , ID,Activity,Activity_type,Activity Status.
Here activity status 11 = Completed, 12 = Incomplete.
Each ID is associated with activity type call1,call2,email1,email2 and text1. I only want to query all the ID with activity Text1 with "Activity Status"=12 when all other "Activity Status" related to that ID = 11.
In the example I would like to get output of Text1 with ID 10 as ID 11 still has email 2 with activity status 12. Any help on this is much appreciated.
Using GROUP BY and HAVING should be a working solution, but your question probably has two possible answers:
case, when "Activity Status" = 12 and all other (all the rest) "Activity Status" = 11
case, when "Activity Status" = 12 and all other (all possible four) "Activity Status" = 11
Input:
CREATE TABLE #Data
(
ID int,
Activity varchar(10),
Activity_type int,
Activity_status int
)
INSERT INTO #Data
(ID, Activity, Activity_type, Activity_status)
VALUES
(10, 'call1', 10893, 11),
(10, 'call2', 10894, 11),
(10, 'email1', 10895, 11),
(10, 'email2', 10896, 11),
(10, 'text1', 10897, 12),
(11, 'call1', 10893, 11),
(11, 'call2', 10894, 11),
(11, 'email1', 10895, 11),
(11, 'email2', 10896, 12),
(11, 'text1', 10897, 12)
T-SQL (for all the rest):
SELECT ID
FROM #Data
GROUP BY ID
HAVING
SUM(CASE WHEN Activity = 'text1' AND Activity_status = 12 THEN 1 END) = 1 AND
SUM(1) - SUM(CASE WHEN Activity <> 'text1' AND Activity_status = 11 THEN 1 END) = 1
T-SQL (for all possible four):
SELECT ID
FROM #Data
GROUP BY ID
HAVING
SUM(CASE WHEN Activity = 'text1' AND Activity_status = 12 THEN 1 END) = 1 AND
SUM(CASE WHEN Activity IN ('call1', 'call2', 'email1', 'email2') AND Activity_status = 11 THEN 1 END) = 4
Output:
---
ID
---
10
You can use group by and having:
select id
from t
group by id
having sum(case when activity = 'Text1' and activity_status = 12 then 1 else 0 end) > 0 and
sum(case when activity <> 'Text1' and activity_status <> 11 then 1 else 0 end) = 0;
Related
I want to select all users from the users' table and their data from the vacation table as start vacation date and end date to get their vacation balance and if the user has no vacation taken return 0
Here is my query:
SELECT
dbo.USERINFO.USERID,
dbo.USERINFO.NAME AS إسم_الموظف,
SUM(DATEDIFF(d, dbo.vacation.STARTSDAY, dbo.vacation.ENDSDAY) + 1) AS عددالايام,
dbo.userinfo.balance - (SUM(DATEDIFF(d, dbo.vacation.STARTSDAY, dbo.vacation.ENDSDAY) + 1)) AS الرصيد
FROM
dbo.USERINFO
LEFT JOIN
dbo.Vacation ON dbo.Vacation.UserID = dbo.USERINFO.USERID
WHERE
dbo.USERINFO.DEFAULTDEPTID <> 7
AND dbo.vacation.DATEID = 2
AND year(dbo.vacation.STARTSDAY) = 2020
AND month(dbo.vacation.STARTSDAY) BETWEEN 1
AND 12
GROUP BY
dbo.USERINFO.userid,
dbo.USERINFO.NAME,
dbo.userinfo.balance
You need to move the condition inside the case when statement while calculating the sum
SELECT U.USERID,
U.NAME AS إسم_الموظف,
ISNULL(SUM(CASE WHEN V.DATEID = 2
AND year(V.STARTSDAY) = 2020
THEN DATEDIFF(d, V.STARTSDAY, V.ENDSDAY)
ELSE 0
END), 0) AS عددالايام,
ISNULL(U.balance, 0) - ISNULL(SUM(CASE WHEN V.DATEID = 2
AND year(V.STARTSDAY) = 2020
THEN DATEDIFF(d, V.STARTSDAY, V.ENDSDAY)
ELSE 0
END), 0) AS الرصيد
FROM dbo.USERINFO AS U
LEFT JOIN dbo.Vacation AS V ON V.UserID = U.USERID
WHERE U.DEFAULTDEPTID <> 7
GROUP BY U.userid,
U.NAME,
U.balance
Results:
+--------+------------+-----------+--------+
| USERID | إسم_الموظف | عددالايام | الرصيد |
+--------+------------+-----------+--------+
| 1 | NAME 1 | 8 | 2 |
+--------+------------+-----------+--------+
| 2 | NAME 2 | 6 | 4 |
+--------+------------+-----------+--------+
| 4 | NAME 4 | 0 | 10 |
+--------+------------+-----------+--------+
The script used to generate the dummy data is:
CREATE TABLE USERINFO (USERID int, NAME VARCHAR(20), BALANCE INT, DEFAULTDEPTID INT)
INSERT INTO USERINFO VALUES
(1, 'NAME 1', 10, 5),
(2, 'NAME 2', 10, 4),
(3, 'NAME 3', 10, 7),
(4, 'NAME 4', 10, 5)
CREATE TABLE Vacation (UserID INT, STARTSDAY DATETIME, ENDSDAY DATETIME, DATEID INT)
INSERT INTO Vacation VALUES
(1, '2020-01-12', '2020-01-20', 2),
(1, '2020-01-22', '2020-01-24', 3),
(2, '2020-01-27', '2020-01-31', 2),
(2, '2020-03-27', '2020-03-29', 2),
(7, '2020-03-27', '2020-03-29', 2)
Note that the UserId 3 is retired so it is not included in the result.
Note that vacation with DateId 3 not included when calculating the sum
Let's say the following tables exist:
a user table with user_id
an item table with item_id
and a vote table with user_id, item_id, value
where value can be the integers 0 through 3. Users can vote on items.
I am looking for a way to perform a query such that I can find all users that have voted the same as an input user with a set of specified items and corresponding vote values.
For example, say I send a request to the app server with the following information:
user_id: 5
votes: [
{ item_id: 7, vote_value: 0 },
{ item_id: 3, vote_value: 3 },
{ item_id: 5, vote_value: 1 },
{ item_id: 41, vote_value: 3 },
{ item_id: 23, vote_value: 2 }
]
I would like to then return all the users that at least share these results (that is, all other users that voted 0 on item 7, voted 3 on item 3, voted 1 on item 5, voted 3 on item 41, and voted 2 on item 23). They can have more votes and other votes on other items, but at least those must match. Also, five voted items passed in is just an example. There are an arbitrary amount.
The only solution I have come up with so far involves a quad join and having a column for all 0 votes, all 1 votes, all 2 votes, and all 3 votes for each user, and then returning that entire dataset to the app-server to perform calculations there to find matches. But I was hoping there is a better, more performant way.
One way to do it
SELECT user_id
FROM votes
GROUP BY user_id
HAVING MAX(CASE WHEN (item_id, value) = (7, 0) THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN (item_id, value) = (3, 3) THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN (item_id, value) = (5, 1) THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN (item_id, value) = (41, 3) THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN (item_id, value) = (23, 2) THEN 1 ELSE 0 END) = 1
SQLFiddle
The HAVING clause can also be expressed in the following manner
...
HAVING MAX(CASE WHEN (item_id, value) = (7, 0) THEN 1 ELSE 0 END)
+ MAX(CASE WHEN (item_id, value) = (3, 3) THEN 1 ELSE 0 END)
+ MAX(CASE WHEN (item_id, value) = (5, 1) THEN 1 ELSE 0 END)
+ MAX(CASE WHEN (item_id, value) = (41, 3) THEN 1 ELSE 0 END)
+ MAX(CASE WHEN (item_id, value) = (23, 2) THEN 1 ELSE 0 END) = 5
SQLFiddle
I have 2 tables #Claims and #ClaimsActivity:
Query:
declare #Claims table (ClaimID int)
insert into #Claims
values (6070), (6080)
declare #ClaimsActivity table
(
Activityid int,
ClaimID int,
Activity int,
ActivityDate datetime,
ClaimStatus int
)
insert into #ClaimsActivity
values (1, 6070, 0, '2017-11-05 20:23:16.640', 0),
(3, 6070, 6, '2017-11-06 13:50:28.203', 0),
(4, 6070, 9, '2017-11-07 13:39:28.410', 0),
(5, 6070, 10, '2017-11-07 13:40:49.980', 0),
(7, 6070, 8, '2017-11-07 15:46:18.367', 1),
(8, 6070, 8, '2017-11-07 16:50:49.543', 1),
(9, 6070, 9, '2017-11-07 16:50:54.733', 0),
(10, 6070, 4, '2017-11-07 16:55:22.135', 0),
(11, 6070, 6, '2017-11-08 18:32:15.101', 0),
(12, 6080, 0, '2017-11-12 11:15:17.199', 0),
(13, 6080, 8, '2017-11-13 09:12:23.203', 1)
select *
from #Claims
select *
from #ClaimsActivity
order by ActivityDate
I need to add 2 columns based on data in #ClaimsActivity: IsReopened and DateReopened
The logic is:
If the last ClaimStatus (based on ActivityDate) = 1 then IsReopened = 0
But if the last ClaimStatus = 0 then it need to go and check whether one of the Activity is = 9 (Claim Reopened)
and if one of the Activity = 9 then IsReopened should = 1 and DateReopened should be the last date when it was reopened
I brought column StatusOfClaim, but I also need IsReopened and DateReopened
select
Claimid,
isnull((select top 1
case when al.ClaimStatus = 1
then 'Closed'
else 'Open'
end
from
#ClaimsActivity al
where
C.ClaimID = al.ClaimID
order by
al.ActivityDate desc), 'Open') as 'Status of Claim',
NULL as 'isReopen',
NULL as 'DateReopened'
from
#Claims c
Desired output should be like this:
There are many different ways you can accomplish this, but here is an example using CROSS APPLY and OUTER APPLY:
SELECT
ClaimID,
CASE WHEN tmp.IsOpen = 1 THEN 'Open' ELSE 'Closed' END AS 'Status of Claim',
CASE WHEN tmp.IsOpen = 1 AND lastReopen.Activityid IS NOT NULL THEN 1 ELSE 0 END AS 'isReopen',
lastReopen.ActivityDate AS 'DateReopened'
FROM #Claims c
CROSS APPLY (
SELECT ISNULL((
SELECT TOP 1 CASE WHEN al.ClaimStatus = 1 THEN 0 ELSE 1 END
FROM #ClaimsActivity al
WHERE c.ClaimID = al.ClaimID
ORDER BY al.ActivityDate DESC
), 1) AS IsOpen
) tmp
OUTER APPLY (
SELECT TOP 1
al.Activityid,
al.ActivityDate
FROM #ClaimsActivity al
WHERE c.ClaimID = al.ClaimID AND al.Activity = 9
ORDER BY al.ActivityDate DESC
) lastReopen
The CROSS APPLY is just used to produce a column that tells us whether a claim is open or closed, and we can reuse this throughout the rest of the query.
The OUTER APPLY is used to grab to the last "reopen" activity for each claim, of which you want the date.
I can't attest to the performance of this query, but this should at least give you the correct results.
I have a table that looks like the following which was created using the following code...
SELECT Orders.ID, Orders.CHECKIN_DT_TM, Orders.CATALOG_TYPE,
Orders.ORDER_STATUS, Orders.ORDERED_DT_TM, Orders.COMPLETED_DT_TM,
Min(DateDiff("n",Orders.ORDERED_DT_TM,Orders.COMPLETED_DT_TM)) AS
Time_to_complete
FROM Orders
GROUP BY Orders.ORDER_ID, Orders.ID,
Orders.CHECKIN_DT_TM, Orders.CATALOG_TYPE, Orders.ORDERED_DT_TM,
Orders.COMPLETED_DT_TM, HAVING (((Orders.CATALOG_TYPE)="radiology");
ID Time_to_complete ... .....
1 5
1 7
1 8
2 23
2 6
3 7
4 16
4 14
I'd like to add to this code which would select the smallest Time_to_complete value per subject ID. Leaving the desired table:
ID Time_to_complete ... .....
1 5
2 6
3 7
4 14
I'm using Access and prefer to continue using Access to finish this code but I do have the option to use SQL Server if this is not possible in Access. Thanks!
I suspect you need correlated subquery :
SELECT O.*, DateDiff("n", O.ORDERED_DT_TM, O.COMPLETED_DT_TM) AS Time_to_complete
FROM Orders O
WHERE DateDiff("n", O.ORDERED_DT_TM, O.COMPLETED_DT_TM) = (SELECT Min(DateDiff("n", O1.ORDERED_DT_TM, O1.COMPLETED_DT_TM))
FROM Orders O1
WHERE O1.ORDER_ID = O.ORDER_ID AND . . .
);
EDIT : If you want unique records then you can do instead :
SELECT O.*, DateDiff("n", O.ORDERED_DT_TM, O.COMPLETED_DT_TM) AS Time_to_complete
FROM Orders O
WHERE o.pk = (SELECT TOP (1) o1.pk
FROM Orders O1
WHERE O1.ORDER_ID = O.ORDER_ID AND . . .
ORDER BY DateDiff("n", O.ORDERED_DT_TM, O.COMPLETED_DT_TM) ASC
);
pk is your identity column that specifies unique entry in Orders table, so you can change it accordingly.
Have a look at this:
DECLARE #myTable AS TABLE (ID INT, Time_to_complete INT);
INSERT INTO #myTable
VALUES (1, 5)
, (1, 7)
, (1, 8)
, (2, 23)
, (2, 6)
, (3, 7)
, (4, 16)
, (4, 14);
WITH cte AS
(SELECT *
, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Time_to_complete) AS RN
FROM #myTable)
SELECT cte.ID
, cte.Time_to_complete
FROM cte
WHERE RN = 1;
Results :
ID Time_to_complete
----------- ----------------
1 5
2 6
3 7
4 14
It uses row numbers over groups, then selects the first row for each group. You should be able to adjust your code to use this technique. If in doubt wrap your entire query in a cte first then apply the technique here.
It's worth becoming familiar with this process as it gets used in a lot of places - especially around de-duping data.
Try This
DECLARE #myTable AS TABLE (ID INT, Time_to_complete INT);
INSERT INTO #myTable
VALUES (1, 5)
, (1, 7)
, (1, 8)
, (2, 23)
, (2, 6)
, (3, 7)
, (4, 16)
, (4, 14);
SELECT O.ID, O.Time_to_complete
FROM #myTable O
WHERE o.Time_to_complete = (Select min(m.Time_to_complete) FROM #myTable m
Where o.id=m.ID
);
Result :
ID Time_to_complete
1 5
2 6
3 7
4 14
Let's say I have the following table tbl_Rules:
RuleID NameOperator NameValues TypeOperator TypeValue
1 NotIn John In 2
1 NotIn Alex In NULL
1 NotIn Mike In NULL
2 In Mike NotIn 2
And my source table looks like this tbl_Source:
ID Name Type Cost
1 Mike 2 100
2 Cole 2 200
3 Ken 1 300
4 Tara 1 400
5 Mike 1 500
6 Sonya 1 600
7 Ann 2 700
8 Mike 1 800
I want to be able to join these two tables and get the following result tbl_Result:
RuleID Name Type Cost
1 Cole 2 200
1 Ann 2 700
2 Mike 1 500
2 Mike 1 800
If I was writing this query manually my query would look like this:
select 1, Name, Type, Cost
from tbl_Source
Where Name not in ('John', 'Alex', 'Mike') and Type in (2)
union all
select 2, Name, Type, Cost
from tbl_Source
where Name in ('Mike') and Type not in (2)
In my current setup tbl_Rule has 500 records and tbl_Source has 500k records.
Any advice on this can be achieved is greatly appreciated. Limitations:
No CLR functions, No 2017 features (e.g. String_agg)
Update: DDL statements for the above sample can be found here: http://sqlfiddle.com/#!18/9a29f/2/0
Here's one way. I have used cross join to check the rules. There may be better ways with dynamic SQL where rules are implemented in join
declare #tbl_Rules table(
RuleID int
, NameOperator varchar(20)
, NameValues varchar(20)
, TypeOperator varchar(20)
, TypeValue int
)
insert into #tbl_Rules
values
(1, 'NotIn', 'John', 'In', 2)
, (1, 'NotIn', 'Alex', 'In', NULL)
, (1, 'NotIn', 'Mike', 'In', NULL)
, (2, 'In', 'Mike', 'NotIn', 2)
declare #tbl_Source table (
ID int
, Name varchar(20)
, Type int
, Cost int
)
insert into #tbl_Source
values
(1, 'Mike', 2, 100)
, (2, 'Cole', 2, 200)
, (3, 'Ken', 1, 300)
, (4, 'Tara', 1, 400)
, (5, 'Mike', 1, 500)
, (6, 'Sonya', 1, 600)
, (7, 'Ann', 2, 700)
, (8, 'Mike', 1, 800)
;with cte as (
select
distinct Ruleid, a.NameOperator, a.TypeOperator
, NameValues = (
select
'!' + b.NameValues
from
#tbl_Rules b
where
a.RuleID = b.RuleID
and b.NameValues is not null
for xml path('')
) + '!'
, TypeValue = (
select
concat('!', b.TypeValue)
from
#tbl_Rules b
where
a.RuleID = b.RuleID
and b.TypeValue is not null
for xml path('')
) + '!'
from
#tbl_Rules a
)
select
b.RuleID, a.Name, a.Type, a.Cost
from
#tbl_Source a
cross join cte b
where
1 = case
when b.NameOperator = 'In' and charindex('!' + a.Name + '!', b.NameValues) > 0 and b.TypeOperator = 'In' and charindex(concat('!', a.Type, '!'), b.TypeValue) > 0 then 1
when b.NameOperator = 'In' and charindex('!' + a.Name + '!', b.NameValues) > 0 and b.TypeOperator = 'Notin' and charindex(concat('!', a.Type, '!'), b.TypeValue) = 0 then 1
when b.NameOperator = 'NotIn' and charindex('!' + a.Name + '!', b.NameValues) = 0 and b.TypeOperator = 'In' and charindex(concat('!', a.Type, '!'), b.TypeValue) > 0 then 1
when b.NameOperator = 'NotIn' and charindex('!' + a.Name + '!', b.NameValues) = 0 and b.TypeOperator = 'NotIn' and charindex(concat('!', a.Type, '!'), b.TypeValue) = 0 then 1
else 0
end
Output:
RuleID Name Type Cost
---------------------------
1 Cole 2 200
1 Ann 2 700
2 Mike 1 500
2 Mike 1 800
You can try this.
SELECT MAX(R_N.RuleID) RuleID, S.Name, S.Type, S.Cost
FROM tbl_Source S
INNER JOIN tbl_Rules R_N ON
(R_N.NameValues <> S.Name and R_N.NameOperator = 'NotIn' )
OR (R_N.NameValues = S.Name and R_N.NameOperator = 'In' )
INNER JOIN tbl_Rules R_S ON
R_S.RuleID = R_N.RuleID AND
(R_S.TypeValue <> S.Type and R_S.TypeOperator = 'NotIn' )
OR (R_S.TypeValue = S.Type and R_S.TypeOperator = 'In' )
GROUP BY
S.Name, S.Type, S.Cost
HAVING
MAX(R_N.NameOperator) = MIN(R_N.NameOperator)
AND MAX(R_S.TypeOperator) = MIN(R_S.TypeOperator)
Result:
RuleID Name Type Cost
----------- -------------------- ----------- -----------
1 Ann 2 700
1 Cole 2 200
2 Mike 1 500
2 Mike 1 800