Ms Sql: query with multiple row criteria - sql

I have a table:
CREATE TABLE Cl
(
PropId int,
ClId int
);
INSERT INTO Cl
(PropId, ClId)
VALUES
(1, 1),
(1, 2),
(1, 3),
(2, 1),
(2, 2),
(3, 1),
(4, 1),
(4, 2),
(5, 1),
(5, 2);
PropId ClId
1 1
1 2
1 3
2 1
2 2
3 1
4 1
4 2
5 1
5 2
I would like to build a query that returns:
PropId
2
4
5
when in my "WHERE" condition I have only PropId=2. The table join must be through ClId values. Thanks in advance. sqlfiddle

You can use group by and having. Here is one method:
select propid
from c1
group by propid
having sum(case when clid = 1 then 1 else 0 end) > 0 and
sum(case when clid = 2 then 1 else 0 end) > 0 and
count(distinct clid) = 2;

As to the comment to the question:
(...) I have only 2 in my PropId and first of all I have to go to the CliId
values and according to them (values and their segmentations) achive
my PropId output. (...)
If by segmentation you mean a set of records that count is equal or greater than 1, you need to join data this way:
SELECT C.PropId
FROM Cl C INNER JOIN (
SELECT B.ClId
FROM Cl B
WHERE B.PropId = 2
) D ON D.Clid = C.Clid
GROUP BY C.PropID
HAVING COUNT(c.ClId )>1
Note: The result will be: {1, 2, 4, 5}. Why?
Because a D subquery returns set of ClId: {1, 2} and there's 4 PropId records which meet criteria.

Related

How to make new data is a function of last data

In SQL server I have a table just like the following table, original, and I want to update where Index ID>3
and the principle is lastaccmulated*2 + movement.
For example
where Index ID =3 accumulated = 8 * 2 + 2 =18
I tried the lag function but it can only be used in select, which means I cannot finish in one update.
Is there any sharp function to make this happen?
Table orginal
IndexID
accumulated
movement
1
5
2
2
8
2
3
0
2
4
0
2
5
0
2
Table what I want after update
IndexID
accumulated
movement
1
5
2
2
8
2
3
18
2
4
38
2
5
78
2
Just like above mention, it went wrong when I use lag function.
Try this:
DROP TABLE IF EXISTS #YOUR_TABLE
SELECT
id,
accumulated,
movement
INTO #YOUR_TABLE
FROM (
VALUES
(1, 5, 2),
(2, 8, 2),
(3, 0, 2),
(4, 0, 2),
(5, 0, 2),
(6, 0, 2)
) src (id, accumulated, movement)
;WITH
CALCULATION AS (
SELECT
id,
2 * accumulated + movement as accumulated
FROM #YOUR_TABLE
WHERE id = 2
UNION ALL
SELECT
yt.id,
2 * c.accumulated + yt.movement as accumulated
FROM CALCULATION c
JOIN #YOUR_TABLE yt ON yt.id = c.id + 1
)
UPDATE yt SET
yt.accumulated = c.accumulated
FROM #YOUR_TABLE yt
JOIN CALCULATION c ON
c.id = yt.id
WHERE
yt.id >= 3
OPTION (MAXRECURSION 0) -- To prevent recursion limitiations
SELECT * FROM #YOUR_TABLE
We are using recursive CTE here. Before UNION ALL we give values for step zero, after we have calculation based on previous step (yt.id = c.id + 1).

T-SQL query suggestion

CREATE TABLE #Emp
(
ID int,
Name varchar(100)
)
INSERT INTO #Emp
VALUES (1, 'AAA'), (2, 'BBB'), (3, 'CCC')
CREATE TABLE #Task
(
EmpID int,
TaskName varchar(100),
[Hours] int
)
INSERT INTO #Task
VALUES (1, 'Task-1', 2), (1, 'Task-2', 4), (1, 'Task-5', 3),
(2, 'Task-3', 2), (2, 'Task-4', 4), (2, 'Task-5', 3),
(3, 'Task-1', 2), (3, 'Task-1', 4), (3, 'Task-1', 6),
(3, 'Task-2', 3), (3, 'Task-6', 1)
#Emp
ID Name
--------
1 AAA
2 BBB
3 CCC
#Task:
EmpID TaskName Hours
-------------------------
1 Task-1 2
1 Task-2 4
1 Task-5 3
2 Task-3 2
2 Task-4 4
2 Task-5 8
3 Task-1 2
3 Task-1 4
3 Task-1 6
3 Task-2 3
3 Task-6 1
For each employee I need to get the sum of hours for (Task-1 and Task-2) and for Task-5
Something like below
Name PrepHours(Task-1 + Task-2) ReviewHours(Task-5)
-------------------------------------------------------
AAA 6 3
BBB 0 8
CCC 15 0
I tried the query shown here, but it fails with error column #Task.TaskName is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
SELECT
Name, PrepHours, ReviewHours
FROM
#Emp AS E
JOIN
(SELECT
empid,
CASE
WHEN Taskname IN ('Task-1','Task-2')
THEN SUM(Hours)
ELSE 0
END AS 'PrepHours',
CASE
WHEN Taskname IN ('Task-5')
THEN SUM(Hours)
ELSE 0
END AS 'ReviewHours'
FROM
#Task
WHERE
Taskname IN ('Task-1', 'Task-2', 'Task-5')
GROUP BY
empid) AS t ON E.id = t.empid
ORDER BY
Name
So if I add the Taskname in the Group by it provides multiple rows for each. I need one row for each employee. Need help please.
Name PrepHours ReviewHours
-------------------------------
AAA 2 0
AAA 4 0
AAA 0 3
BBB 0 8
CCC 12 0
CCC 3 0
You can create your totals using a conditional case espression in a cross apply
select e.name, t.*
from #emp e
cross apply (
select
Sum(case when taskname in ('task-1','task-2') then hours else 0 end) PrepHours,
Sum(case when taskname ='Task-5' then hours else 0 end) ReviewHours
from #task t
where t.EmpId=e.Id
)t
You can make the original query work by moving the Sum outside the case statement:
SELECT [Name],
PrepHours,
ReviewHours
FROM #Emp AS E
JOIN (SELECT empid,
Sum( CASE
WHEN Taskname IN ( 'Task-1', 'Task-2' ) THEN [Hours]
ELSE 0
END) AS 'PrepHours',
sum(CASE
WHEN Taskname IN ( 'Task-5' ) THEN [Hours]
ELSE 0
END) AS 'ReviewHours'
FROM #Task
WHERE Taskname IN ( 'Task-1', 'Task-2', 'Task-5' )
GROUP BY empid) AS t
ON E.id = t.empid
ORDER BY Name

How to get conditional SUM?

I am trying to get a conditional sum based on another column. For example, suppose I have this dataset:
ID Date Type Total
-----------------------
5 12/16/2019 0 7
5 12/16/2019 1 0
5 12/17/2019 0 7
5 12/17/2019 1 7
5 12/18/2019 0 7
5 12/18/2019 1 0
5 12/19/2019 0 7
5 12/19/2019 1 7
5 12/20/2019 0 7
5 12/20/2019 1 7
5 12/23/2019 0 7
5 12/24/2019 0 7
5 12/25/2019 0 7
5 12/26/2019 0 7
5 12/27/2019 0 7
If there is a type of 1 then I only want that data for that data, else if there is only 0 then I want that data for that date.
So for 12/16/2019 I would want the value 0. For 12/23/2019 - 12/27/2019 I would want the value 7.
You can use row_number() :
select t.*
from (select t.*, row_number() over (partition by id, date order by type desc) as seq
from table t
) t
where seq = 1;
A simple ROW_NUMBER can handle this quite easily. I changed some of the column names because reserved words are just painful to work with.
declare #Something table
(
ID int
, SomeDate Date
, MyType int
, Total int
)
insert #Something values
(5, '12/16/2019', 0, 7)
, (5, '12/16/2019', 1, 0)
, (5, '12/17/2019', 0, 7)
, (5, '12/17/2019', 1, 7)
, (5, '12/18/2019', 0, 7)
, (5, '12/18/2019', 1, 0)
, (5, '12/19/2019', 0, 7)
, (5, '12/19/2019', 1, 7)
, (5, '12/20/2019', 0, 7)
, (5, '12/20/2019', 1, 7)
, (5, '12/23/2019', 0, 7)
, (5, '12/24/2019', 0, 7)
, (5, '12/25/2019', 0, 7)
, (5, '12/26/2019', 0, 7)
, (5, '12/27/2019', 0, 7)
select ID
, SomeDate
, MyType
, Total
from
(
select *
, RowNum = ROW_NUMBER()over(partition by SomeDate order by MyType)
from #Something
) x
where x.RowNum = 1
You can do this with simple aggregation . . . well, and case:
select id, date, max(type),
coalesce(max(case when type = 1 then total end),
max(total)
) as total
from t
group by id, date;
This formulation is assuming that you have only types 0 and 1 and at most one of each type on each day for a given id.

SQL return the most recent record of a specific type if a condition is met

Hopefully I don't make this complicated.
I wrote the following SQL that returns the users that their most recent transactions meet a condition
(TRANS_TYPE NOT IN (4, 6, 21, 23) OR DEPOSIT_OPTION & 64 <> 64).
SELECT
TERMINAL_ID,REGISTER_ID,[USER_ID],sub.CREATE_DATE,sub.TRANS_TYPE_ID,BUS_DATE_ID
FROM (
SELECT
T.TERMINAL_ID
,US.REGISTER_ID
,U.[USER_ID]
,T.CREATE_DATE
,T.TRANS_TYPE_ID
,T.BUS_DATE_ID
,T.TRANS_CONFIG_ID
,TT.TRANS_TYPE
, row_number()
over (partition by U.[USER_ID]
order by T.CREATE_DATE desc) rn
From [RCMDYNAMIC].[dbo].[Transaction] T
INNER JOIN [RCMDYNAMIC].[dbo].[UserSession] US ON T.USER_SESSION_ID = US.USER_SESSION_ID
INNER JOIN [RCMSTATIC].[dbo].[User] U ON U.[USER_ID] = US.[USER_ID]
INNER JOIN [RCMSTATIC].[dbo].[TransactionType] TT ON T.TRANS_TYPE_ID = TT.TRANS_TYPE_ID
INNER JOIN [RCMSTATIC].[dbo].[Register] R ON US.REGISTER_ID = R.REGISTER_ID
) sub
LEFT JOIN
[RCMSTATIC].[dbo].[DepositConfig] DC
ON sub.TRANS_CONFIG_ID = DC.DEPOSIT_ID
WHERE sub.rn = 1 AND (TRANS_TYPE NOT IN (4, 6, 21, 23) OR DEPOSIT_OPTION & 64 <> 64)
I acquired the " most recent transaction" by using
row_number()
over (partition by U.[USER_ID]
order by T.CREATE_DATE desc) rn
However, what I really want is selecting the most recent transaction of TRANS_TYPE = 10 IF the most recent transaction met the previous condition.
The sub query in the previous code will return all transactions for all users and rank them in DESC order and the outer SELECT will display the users that meet the condition by checking their Rank 1 transaction.
What I want is having something like this
FOREACH user
IF the user rank 1 transaction meet the condition
THEN
FIND the user most recent transaction of TRANS_TYPE 10
it could be the transaction rank 1 or rank N
Example:
User_ID TRANS_TYPE DEPOSIT_OPTION Rank
1 4 7 1
1 10 7 2
2 22 64 1
2 23 4 2
2 10 126 3
2 4 7 4
3 10 3 1
4 6 64 1 -- doesn't meet the condition
4 10 7 2
form the previous results if the Rank 1 row satisfies the condition
WHERE sub.rn = 1 AND (TRANS_TYPE NOT IN (4, 6, 21, 23) OR DEPOSIT_OPTION & 64 <> 64)
I want the TRANS_TYPE= 10 to be displayed so I would expect the result to be:
User_ID TRANS_TYPE Rank
1 10 2
2 10 3
3 10 1
I am sorry if the question is not very clear I tried my best!
drop table if exists dbo.Deposits;
create table dbo.Deposits (
User_ID int
, Trans_Type int
, Deposit_Option int
, Rank int
);
insert into dbo.Deposits (User_ID, Trans_Type, Deposit_Option, Rank)
values (1, 4, 7, 1), (1, 10, 7, 2)
, (2, 22, 64, 1), (2, 23, 4, 2), (2, 10, 126, 3), (2, 4, 7, 4)
, (3, 10, 3, 1)
, (4, 6, 64, 1), (4, 10, 7, 2);
select
sub2.User_ID, sub2.Trans_Type, sub2.Rank
from dbo.Deposits sub
inner join dbo.Deposits sub2 on sub.User_ID = sub2.User_ID
and sub2.Trans_Type = 10
where sub.Rank = 1 AND (sub.Trans_Type NOT IN (4, 6, 21, 23) OR sub.Deposit_Option & 64 <> 64)

SQL Server 2008 count

I have created a simple database to store test results, but I am struggling with the SQL Count to total up my pass/fail/warning to present this.
The idea is that a test run (TRID) will have severaltest sets (TSID), each with several test cases (TCID).
Total should equal amount of test cases for that TSID
Pass should equal amount of test cases with all steps of StatusID=1
Fail should equal amount of test cases with 1 or more step of StatusID=2.
Warning should equal amount of test cases with 1 or more step of StatusID=3, but the same test cases should have zero fails in it. If there is a failed step, then test case should fail as per above.
SQL to create a simplified example of my Results table:-
create table Results (StatusID int, TRID int, TSID int, TCID int);
--Test Set 1 / Test Case 1.
INSERT INTO Results VALUES (1, 1, 1, 1)
INSERT INTO Results VALUES (1, 1, 1, 1)
INSERT INTO Results VALUES (1, 1, 1, 1)
--Test Set 1 / Test Case 2
INSERT INTO Results VALUES (1, 1, 1, 2)
INSERT INTO Results VALUES (1, 1, 1, 2)
--Test Set 2 / Test Case 1
INSERT INTO Results VALUES (1, 1, 2, 1)
INSERT INTO Results VALUES (1, 1, 2, 1)
INSERT INTO Results VALUES (1, 1, 2, 1)
--Test Set 2 / Test Case 2
INSERT INTO Results VALUES (1, 1, 2, 2)
INSERT INTO Results VALUES (2, 1, 2, 2)
--Test Set 3 / Test Case 1
INSERT INTO Results VALUES (1, 1, 3, 1)
INSERT INTO Results VALUES (1, 1, 3, 1)
INSERT INTO Results VALUES (1, 1, 3, 1)
--Test Set 3 / Test Case 2
INSERT INTO Results VALUES (1, 1, 3, 2)
INSERT INTO Results VALUES (3, 1, 3, 2)
--Test Set 4 / Test Case 1
INSERT INTO Results VALUES (1, 1, 4, 1)
INSERT INTO Results VALUES (1, 1, 4, 1)
INSERT INTO Results VALUES (1, 1, 4, 1)
--Test Set 4 / Test Case 2
INSERT INTO Results VALUES (3, 1, 4, 2)
INSERT INTO Results VALUES (2, 1, 4, 2)
SELECT * FROM Results
My current SQL (which you will see provides me with the wrong warning count:-
DECLARE #trid INT
SET #trid = 1
SELECT TRID, TSID,
(SELECT COUNT(DISTINCT TCID) FROM Results WHERE R.TRID = #trID AND R.TSID = TSID) As Total,
(SELECT COUNT(DISTINCT TCID) FROM Results WHERE TRID = #trID AND R.TSID = TSID) - (SELECT COUNT(DISTINCT TCID) FROM Results WHERE TRID = #trID AND R.TSID = TSID AND StatusID = 2) - (SELECT COUNT(DISTINCT TCID) FROM Results WHERE TRID = #trID AND R.TSID = TSID AND StatusID = 3 AND (SELECT COUNT(DISTINCT TCID) FROM Results WHERE TRID = #trID AND R.TSID = TSID AND StatusID = 2) = 0) AS Pass,
(SELECT COUNT(DISTINCT TCID) FROM Results WHERE R.TSID = TSID AND StatusID=2) As Fail,
(SELECT COUNT(DISTINCT TCID) FROM Results WHERE R.TSID = TSID AND StatusID=3) As Warning
FROM Results R
WHERE TRID = #TRID
GROUP BY TRID, TSID
From the above SQL, the current incorrect results are:-
TRID TSID Total Pass Fail Warning
1 1 2 2 0 0
1 2 2 1 1 0
1 3 2 1 0 1
1 4 2 1 1 1*
Results should be....
TRID TSID Total Pass Fail Warning
1 1 2 2 0 0
1 2 2 1 1 0
1 3 2 1 0 1
1 4 2 1 1 0*
Thanks
You could calculate the statistics per test case (TCID) in a subquery. The outer query could then calculate the statistics per test set (TSID). For example:
select TRID
, TSID
, count(*) as Total
, sum(case when FailSteps = 0 and WarnSteps = 0 then 1 else 0 end) as Pass
, sum(case when FailSteps > 0 then 1 else 0 end) as Fail
, sum(case when FailSteps = 0 and WarnSteps > 0 then 1 else 0 end) as Warning
from (
select TRID
, TSID
, TCID
, sum(case when StatusID = 1 then 1 else 0 end) as PassSteps
, sum(case when StatusID = 2 then 1 else 0 end) as FailSteps
, sum(case when StatusID = 3 then 1 else 0 end) as WarnSteps
from Results
group by
TRID
, TSID
, TCID
) as TestCases
group by
TRID
, TSID
Live example at SQL Fiddle.