SQL select & Group if at least one exists - sql

I need to select & Group Id & Pid when at least 1 for each Pid in Id has IsExists=1
Id Pid Opt IsExists
27 2 107 1
27 2 108 0
27 5 96 1
51 9 17 1
51 9 18 0
51 10 112 0
758 25 96 0
758 97 954 1
758 194 2902 1
758 194 2903 1
The result should be:
Id IsExists
27 1
In the result for [id=27 | pid=2] & for [id=27 | pid=5] has at least 1 with isExists=1
Is it possible?

One method uses two levels of aggregation:
select id
from (select id, pid, max(isexists) as max_isexists
from t
group by id, pid
) t
having count(*) = sum(max_isexists);
This assumes that isexists takes on the values 0 and 1.
An alternative only uses one level of aggregation but is a bit trickier, using count(distinct):
select id
from t
group by id
having count(distinct pid) = count(distinct case when isexists = 1 then pid end);

You need a nested aggregation:
select Id
from
(
select Id, Pid,
-- returns 1 when value exists
max(IsExists) as maxExists
from tab
group by Id, Pid
) as dt
group by Id
-- check if all Pid got a 1
having min(maxExists) = 1

Try this ... it uses the inner group by to get the distinct counts of IsExists by ID and PID and the outer one checks if there are 2 or more
SELECT ID, 1 as IsExists FROM
(
select ID, PID , Count(Distinct IsExists) as IsExists
FROM
(
Select 27 as ID , 2 as PID , 1 as IsExists UNION ALL
Select 27 as ID , 2 as PID , 0 as IsExists UNION ALL
Select 27 as ID , 5 as PID , 1 as IsExists UNION ALL
Select 51 as ID , 9 as PID , 1 as IsExists UNION ALL
Select 51 as ID , 9 as PID , 0 as IsExists UNION ALL
Select 51 as ID , 10 as PID , 0 as IsExists
) a
WHERE IsExists = 1
Group by ID, PID
) B
GROUP BY B.ID
Having Count(*) >= 2

Related

How to find most frequent code(varchar) from a table

I would like to find most frequent code within CodeID which is in same code_group from a table.
For example, from original table
ID CodeID Name Code Code_group
1 1 A 101 0
2 1 A 102 0
3 1 B 102 0
4 2 C 201 0
5 2 C 201 0
6 2 D 202 0
7 2 E 202 0
8 3 F 101 1
9 3 G 103 1
10 3 G 104 1
11 3 G 104 1
I want output like the below.
ID CodeID Name Code Code_group Selected_code
1 1 A 101 0 102
2 1 A 102 0 102
3 1 B 102 0 102
4 2 C 201 0 NULL
5 2 C 201 0 NULL
6 2 D 202 0 NULL
7 2 E 202 0 NULL
8 3 F 101 1 104
9 3 G 103 1 104
10 3 H 104 1 104
11 3 H 104 1 104
Even though code of 8th ID is same in CodeID: 1,it is not in the same Code_group.
So For CodeID: 1, Selected_code would be 102.
it must be counted within exactly same Code_group.
=======================================
I have tried it like the below. I should not use ID for this one.
From TableA
with m as
(
select
CodeID,
Name,
Code,
Code_group,
cnt,
Selected_code = ROW_NUMBER() over (partition by Code_group order by cnt desc)
from( select CodeID, Name, Code,Code_group
,count(*) over (partition by Code,CodeID) as cnt from tableA
group by CodeID, Name, Code, Code_group,
) as t
group by CodeID,
Name,
Code,
Code_group, cnt
)
select a.CodeID,
a.Name,
a.Code,
a.Code_group, b.Code as Selected_code, cnt
from(select
CodeID,
Name,
Code,
Code_group,Selected_code,
cnt
from m) as a left outer join
(select CodeID,
Name,
Code,
Code_group,Selected_code,
cnt
from m where selected_Code=1) as b on a.CodeID = b.CodeID and a.Code_Group = b.Code_Group
order by a.CodeID, a.Code_Group
The problem of this is
With statment makes my table distinct. It shows only one row if there is exactly same data such as ID 1,2.
Also, I cannot make NULL if there is exactly same frequencies.
What should I add to get my desired output?
Or is there any better approach for this?
CTE cte find the highest frequency code by Code_group and CodeID using dense_rank()
CTE selected check for any Code with same frequency and exclude them.
Final query just select from the original table and LEFT JOIN the selected
with
cte as
(
select Code_group, CodeID, Code
from
(
select Code_group, CodeID, Code,
r = dense_rank() over (partition by Code_Group, CodeID
order by count(*) desc)
from tableA
group by Code_group, CodeID, Code
) c
where c.r = 1
),
selected as
(
select Code_group, CodeID, Code
from
(
select Code_group, CodeID, Code,
cnt = count(*) over (partition by Code_group, CodeID)
from cte
) s
where s.cnt = 1
)
select a.*,
Selected_Code = s.Code
from tableA a
left join selected s on a.Code_Group = s.Code_Group
and a.CodeID = s.CodeID;
db<>fiddle demo

How to select RANK over condition (depending previous row)

What we have
I have table like this
id PlayerId Amount
----------- ----------- -----------
1 1 10
2 1 20
3 1 30
4 1 40
5 1 40
11 1 20
13 1 20
15 1 40
14 2 19
12 2 10
6 2 1
7 2 5
8 2 10
9 2 20
10 2 30
I have to select only rows where amount greater than previous row amount (per player).
So here is a query
SELECT a.id,
a.PlayerId,
a.Amount,
a.PreVval,
a.NextVal
FROM (SELECT id,
PlayerId,
Amount,
LAG(Amount) OVER (PARTITION BY PlayerId ORDER BY id) PreVval,
lead(Amount) OVER (PARTITION BY PlayerId ORDER BY id) NextVal
FROM dbo.Bets ) a
WHERE a.Amount > a.PreVval OR a.Amount < a.NextVal OR (a.PreVval IS NULL AND a.Amount < a.NextVal)
ORDER BY a.PlayerId, a.id
id PlayerId Amount PreVval NextVal
----------- ----------- ----------- ----------- -----------
1 1 10 NULL 20
2 1 20 10 30
3 1 30 20 40
4 1 40 30 40
13 1 20 20 40
15 1 40 20 NULL
6 2 1 NULL 5
7 2 5 1 10
8 2 10 5 20
9 2 20 10 30
10 2 30 20 10
12 2 10 30 19
14 2 19 10 NULL
Question
So now i need to select sets where increase step count > 4 , i mean 1,2,3,4 for player 1 and 6,7,8,9,10 for player 2
Query should run over 15m rows
The following query is an example where you can set the "step_count".
WITH Bets(id,PlayerId,Amount)
AS
(
SELECT 1,1,10 UNION ALL
SELECT 2,1,20 UNION ALL
SELECT 3,1,30 UNION ALL
SELECT 4,1,40 UNION ALL
SELECT 5,1,40 UNION ALL
SELECT 11,1,20 UNION ALL
SELECT 13,1,20 UNION ALL
SELECT 15,1,40 UNION ALL
SELECT 14,2,19 UNION ALL
SELECT 12,2,10 UNION ALL
SELECT 6,2,1 UNION ALL
SELECT 7,2,5 UNION ALL
SELECT 8,2,10 UNION ALL
SELECT 9,2,20 UNION ALL
SELECT 10,2,30
)
,split_ranges
as(
select *,case when lag(amount) over(partition by playerid order by id) > amount
or lag(amount) over(partition by playerid order by id) is null
then row_number() over(partition by playerid order by id)
end as rnk_val
from bets
)
,groups_data
as(
select *
,max(rnk_val) over(partition by playerid order by id) as fill_ranges
from split_ranges
)
select * from (
select *,count(*) over(partition by playerid,fill_ranges) as cnt
from groups_data
)x
where x.cnt>=4
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=6bd815da2cbfa8f65bc999e5736f2041
The following logic is bit tricky, but you can check this out-
DEMO HERE
WITH CTE
AS
(
SELECT * FROM
(
SELECT id,
PlayerId,
Amount,
LAG(Amount) OVER (PARTITION BY PlayerId ORDER BY id) PreVval,
lead(Amount) OVER (PARTITION BY PlayerId ORDER BY id) NextVal,
ISNULL(LAG(ID,3) OVER (PARTITION BY PlayerId ORDER BY id),0) LAG3,
ISNULL(LAG(ID,2) OVER (PARTITION BY PlayerId ORDER BY id),0) LAG2,
ISNULL(LAG(ID,1) OVER (PARTITION BY PlayerId ORDER BY id),0) LAG1,
ISNULL(LEAD(ID,1) OVER (PARTITION BY PlayerId ORDER BY id),0) LEAD1,
ISNULL(LEAD(ID,2) OVER (PARTITION BY PlayerId ORDER BY id),0) LEAD2,
ISNULL(LEAD(ID,3) OVER (PARTITION BY PlayerId ORDER BY id),0) LEAD3
FROM Bets
) a
WHERE a.Amount > a.PreVval
OR a.Amount < a.NextVal
OR (a.PreVval IS NULL AND a.Amount < a.NextVal)
)
SELECT id,PlayerId,Amount,PreVval,NextVal
FROM CTE A
WHERE ID-LAG3 = 3
OR LEAD1 - LAG2 = 3
OR LEAD2 - LAG1 = 3
OR LEAD3 - ID = 3

How to update records belongs to same partition SQL Server

I have a table in a database containing the following data:
GroupId ExceptionId ParentExceptionId row
1 101 NULL 1
1 102 NULL 2
1 103 NULL 3
2 104 NULL 1
2 105 NULL 2
2 106 NULL 3
3 107 NULL 1
3 108 NULL 2`
I worte a following query to get the above row number:
with CTE_RN as
(
SELECT a.[GroupId], a.[SolId], a.[id],ParentExceptionId,
ROW_NUMBER() OVER(PARTITION BY a.[GroupId] ORDER BY a.[GroupId]) AS [row]
FROM [dbo].[trn_Report6_Zone1_Exception] a)
select * from cte_rn`
expected output:
update ParentExceptionId with ExceptionId of first record having same group id and keep ParentExceptionId of that first record null.
GroupId ExceptionId ParentExceptionId row
1 101 NULL 1
1 102 101 2
1 103 101 3
2 104 Null 1
2 105 104 2
2 106 104 3
3 107 NULL 1
3 108 107 2`
You can use first_value function :
select GroupId, ExceptionId,
(case when f_value <> ExceptionId then f_value end) as ParentExceptionId, row
from (select *, first_value(ExceptionId) over (partition by GroupId order by ExceptionId) f_value
from [dbo].[trn_Report6_Zone1_Exception] a
) a;
In same way you can use updateable cte :
with a as (
select *, first_value(ExceptionId) over (partition by GroupId order by ExceptionId) f_value
from [dbo].[trn_Report6_Zone1_Exception] a
)
update a
set ParentExceptionId = f_value
where f_value <> ExceptionId;
Try like this
SELECT * INTO #TAB FROM
(select 1,101,NULL,1 UNION ALL
select 1,102,NULL,2 UNION ALL
select 1,103,NULL,3 UNION ALL
select 2,104,NULL,1 UNION ALL
select 2,105,NULL,2 UNION ALL
select 2,106,NULL,3 UNION ALL
select 3,107,NULL,1 UNION ALL
select 3,108,NULL,2
)AS TABLEA(GroupId,ExceptionId,ParentExceptionId,rowW)
;WITH CTE AS(
SELECT GroupId, MIN(ExceptionId) MIN_ExceptionId
FROM #TAB
GROUP BY GroupId
)
UPDATE T SET T.ParentExceptionId = C.MIN_ExceptionId
FROM #TAB T
INNER JOIN CTE C ON T.GroupId = C.GroupId
WHERE rowW <>1

Aggregate within a group of unchanged values

I have sample data:
RowId TypeId Value
1 1 34
2 1 53
3 1 34
4 2 43
5 2 65
6 16 54
7 16 34
8 1 45
9 6 43
10 6 34
11 16 64
12 16 63
I want to count row for each type (The Value does not matter to me), but only for... neighbor TypeId
TypeId Count
1 3
2 2
16 2
1 1
6 2
16 2
How to achieve this result?
This should give you COUNT of rows within a group of unchanged values:
SELECT TypeId, grp, COUNT(*) FROM (
SELECT RowId, TypeId , Value, gap, SUM(gap) over (ORDER BY RowId ) grp
FROM (SELECT RowId, TypeId , Value,
CASE WHEN TypeId = lag(TypeId) over (ORDER BY RowId )
THEN 0
ELSE 1
END gap
FROM dummy
) t
) tt
GROUP BY TypeId, grp;
If you prefer WITH over endless sub-query inclusions:
WITH dummy_with_groups AS (
SELECT RowId, TypeId , Value, SUM(gap) OVER (ORDER BY RowId) grp
FROM (SELECT RowId, TypeId , Value,
CASE WHEN TypeId = lag(TypeId) OVER (ORDER BY RowId)
THEN 0 ELSE 1 END gap
FROM dummy) t
)
SELECT TypeId, COUNT(*) as Result
FROM dummy_with_groups
GROUP BY TypeId, grp;
http://www.sqlfiddle.com/#!6/f16e9/34
Check this fiddle demo. I have renamed your columns a little.
WITH myCTE AS
(SELECT row_id,
type_id,
ROW_NUMBER () OVER (PARTITION BY type_id ORDER BY row_id)
AS cnt,
CASE LEAD (type_id) OVER (ORDER BY row_id)
WHEN type_id THEN 0
ELSE 1
END
AS show
FROM dummy),
innerQuery AS
(SELECT row_id, type_id, cnt
FROM myCTE
WHERE show = 1)
SELECT iq1.type_id, iq1.cnt - ISNULL (iq2.cnt, 0) CNT
FROM innerQuery iq1
LEFT OUTER JOIN innerQuery iq2
ON iq1.type_id = iq2.type_id
AND EXISTS
(SELECT 1
FROM innerQuery iq3
WHERE iq3.type_id = iq1.type_id
AND iq3.row_id < iq1.row_id
HAVING MAX (iq3.row_id) = iq2.row_id)
The output is exactly as expected.

Help needed in pivoting (SQL Server 2005)

I have a table like
ID Grps Vals
--- ---- -----
1 1 1
1 1 3
1 1 45
1 2 23
1 2 34
1 2 66
1 3 10
1 3 17
1 3 77
2 1 144
2 1 344
2 1 555
2 2 11
2 2 22
2 2 33
2 3 55
2 3 67
2 3 77
The desired output being
ID Record1 Record2 Record3
--- ------- ------- -------
1 1 23 10
1 3 34 17
1 45 66 77
2 144 11 55
2 344 22 67
2 555 33 77
I have tried(using while loop) but the program is running slow. I have been asked to do so by using SET based approach. My approach so far is
SELECT ID,[1] AS [Record1], [2] AS [Record2], [3] as [Record3]
FROM (
Select
Row_Number() Over(Partition By ID Order By Vals) records
,*
From myTable)x
PIVOT
(MAX(vals) FOR Grps IN ([1],[2],[3])) p
But it is not working.
Can any one please help to solve this.(SQL SERVER 2005)
You were almost there! All I had to do was add the necessary columns to the Partition By and Order By clauses, and it worked:
SELECT ID,[1] AS [Record1], [2] AS [Record2], [3] as [Record3]
FROM (
Select
Row_Number() Over(Partition By id, grps Order By id, grps, vals) records
,*
From myTable)x
PIVOT
(MAX(vals) FOR Grps IN ([1],[2],[3])) p
A simpler approach that would not involve the use of PIVOT would something like:
;With ItemGroups As
(
Select Id, Grps, Vals
, Row_Number() Over ( Partition By Id, Grps Order By Vals ) As RowNum
From myTable
)
Select Id
, Max( Case When Grps = 1 Then Vals End )
, Max( Case When Grps = 2 Then Vals End )
, Max( Case When Grps = 3 Then Vals End )
From ItemGroups
Group By Id, RowNum
Order By Id
This may not apply to you but if your using a while loop like....
while( x<Total.length() ){
....do something
}
you should declare Total.length outside of the loop assigned to a variable...
int spoon= Total.length();
while( x< spoon ){
....do something
}
or else
you calculate the number Total.length()every time the loop runs