How to get rows In each Group with minimum order group and special name in SQL Server - sql

i have a table similar this
CREATE TABLE [dbo].[Test](
[Name] [NCHAR](10) NULL,
[GroupId] [INT] NULL,
[GroupOrder] [INT] NULL
)
and below values
INSERT INTO [dbo].[Test]([Name],[GroupId],[GroupOrder]) VALUES ('A',1,1)
INSERT INTO [dbo].[Test]([Name],[GroupId],[GroupOrder]) VALUES ('A-1',1,2)
INSERT INTO [dbo].[Test]([Name],[GroupId],[GroupOrder]) VALUES ('B',2,1)
INSERT INTO [dbo].[Test]([Name],[GroupId],[GroupOrder]) VALUES ('B',2,2)
INSERT INTO [dbo].[Test]([Name],[GroupId],[GroupOrder]) VALUES ('B-1',2,3)
INSERT INTO [dbo].[Test]([Name],[GroupId],[GroupOrder]) VALUES ('C',3,1)
INSERT INTO [dbo].[Test]([Name],[GroupId],[GroupOrder]) VALUES ('C-1',3,2)
INSERT INTO [dbo].[Test]([Name],[GroupId],[GroupOrder]) VALUES ('C-1',3,3)
INSERT INTO [dbo].[Test]([Name],[GroupId],[GroupOrder]) VALUES ('D',4,1)
INSERT INTO [dbo].[Test]([Name],[GroupId],[GroupOrder]) VALUES ('D',4,2)
i need output like this: rows In each Group with minimum order group and name field contain "-1"
Name GroupId GroupOrder
A-1 1 2
C-1 3 2
B-1 2 3

You could use charindex, top 1 with ties, and row_number
select Top (1) with ties
*
from Test t
where charindex('-1', t.Name ) > 0
order by row_number() over(partition by t.GroupId order by t.GroupOrder)
Demo link: http://rextester.com/RBBKX12749

You can use this
Select * from
(
select *,Row_Number() Over(Partition by GroupId Order by GroupOrder asc) as rn
From yourtable
Where Name like '%-1%'
) a
Where rn = 1

CREATE TABLE [dbo].[#Test](
[Name] [NCHAR](10) NULL,
[GroupId] [INT] NULL,
[GroupOrder] [INT] NULL
)
INSERT INTO [dbo].[#Test]([Name],[GroupId],[GroupOrder]) VALUES ('A',1,1)
INSERT INTO [dbo].[#Test]([Name],[GroupId],[GroupOrder]) VALUES ('A-1',1,2)
INSERT INTO [dbo].[#Test]([Name],[GroupId],[GroupOrder]) VALUES ('B',2,1)
INSERT INTO [dbo].[#Test]([Name],[GroupId],[GroupOrder]) VALUES ('B',2,2)
INSERT INTO [dbo].[#Test]([Name],[GroupId],[GroupOrder]) VALUES ('B-1',2,3)
INSERT INTO [dbo].[#Test]([Name],[GroupId],[GroupOrder]) VALUES ('C',3,1)
INSERT INTO [dbo].[#Test]([Name],[GroupId],[GroupOrder]) VALUES ('C-1',3,2)
INSERT INTO [dbo].[#Test]([Name],[GroupId],[GroupOrder]) VALUES ('C-1',3,3)
INSERT INTO [dbo].[#Test]([Name],[GroupId],[GroupOrder]) VALUES ('D',4,1)
INSERT INTO [dbo].[#Test]([Name],[GroupId],[GroupOrder]) VALUES ('D',4,2)
select * from #Test
Select * from
(
select *,Row_Number() Over(Partition by Name Order by GroupOrder asc) as rn
From #Test
Where Name like '%[-1]%'
) a
Where rn = 1
ORDER BY GroupOrder
OUTPUT
Name GroupId GroupOrder
A-1 1 2
B-1 2 3
C-1 3 2

Use ROW_NUMBER window function
Select * from
(
select *,Row_Number() Over(Partition by Name Order by GroupOrder asc) as rn
From yourtable
Where rtrim(Name) like '[A-Z]-1'
) a
Where rn = 1
If the Name should end with -1 then use Where Name like '%-1'. If Name just contains -1 then Where Name like '%-1%'

Related

Can I use lag function in an update function to update a variable table

I have this #variable table with an initial row. I'd like to update the following rows based on that first row.
DECLARE #varTable1 Table
(
[id] [int],
[field1] [decimal](18,4)
)
INSERT INTO #varTable1
VALUES
(1,20),
(1,NULL),
(1,NULL),
(1,NULL)
SELECT * FROM #varTable1
Let's just say I want to multiply field1 by 2. So the expected values following inital row would be
1 20
2 40
3 60
4 80
One way is to get the min value and multiple it by a row number - assuming your id column should actually be incrementing rather than all 1's e.g.
WITH cte AS (
SELECT *
, MIN(field1) OVER () * ROW_NUMBER() OVER (ORDER BY id ASC) newField1
FROM #varTable1
)
UPDATE cte SET field1 = newField1;
I guess id is important so you have to multiple field of same id
DECLARE #varTable1 Table
(
[id] [int],
[field1] [decimal](18,4)
)
INSERT INTO #varTable1
VALUES
(1,20),
(1,NULL),
(1,NULL),
(1,NULL),
(2,NULL)
;with CTE as
(
SELECT *,row_number()over(order by id)rn FROM #varTable1
),
CTE1 as
(
select id, min([field1])Minfield from #varTable1
group by [id]
)
select vt.id,vt.field1,c.Minfield*rn from CTE VT
inner join CTE1 c on vt.id=c.id

How to Use Exists in self join

I want those Id whose Orgorder never equal to 1.
CREATE TABLE [dbo].[TEST](
[ORGORDER] [int] NULL,
[Id] [int] NOT NULL,
[ORGTYPE] [varchar](30) NULL,
ORGID INT NULL,
[LEAD] [decimal](19, 2) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[TEST] ([ORGORDER], [Id], [ORGTYPE] ,ORGID, [LEAD]) VALUES (1, 100, N'ABC',1, NULL)
GO
INSERT [dbo].[TEST] ([ORGORDER], [Id], [ORGTYPE],ORGID, [LEAD]) VALUES (0, 100, N'ABC',2, 0)
GO
INSERT [dbo].[TEST] ([ORGORDER], [Id], [ORGTYPE],ORGID, [LEAD]) VALUES (0, 100, N'ACD',1, NULL)
GO
INSERT [dbo].[TEST] ([ORGORDER], [Id], [ORGTYPE],ORGID, [LEAD]) VALUES (0, 101, N'ABC',0, 0)
GO
INSERT [dbo].[TEST] ([ORGORDER], [Id], [ORGTYPE],ORGID, [LEAD]) VALUES (2, 101, N'ABC',4, NULL)
GO
I am using exists but getting my result.
Expected result is -
ID
101
You can do this with one pass of the data, and order all ORGORDER = 1 first, then if it's the first row and it has the ORGORDER value you want to exclude, you can just ignore it.
;WITH x AS
(
SELECT Id, rn = ROW_NUMBER() OVER
(PARTITION BY Id ORDER BY CASE WHEN ORGORDER = 1 THEN 1 ELSE 2 END)
FROM dbo.TEST
)
SELECT Id FROM x WHERE rn = 1 AND ORGORDER <> 1;
Example db<>fiddle
Use a subquery in a NOT EXISTS clause, linking the subquery table to the outer query table by ID:
SELECT DISTINCT T1.ID
FROM dbo.TEST AS T1
WHERE NOT EXISTS (
SELECT *
FROM dbo.TEST AS T2
WHERE T1.ID = T2.ID
AND T2.ORGORDER = 1
)
db<>fiddle
An option would be using an aggregation with a suitable HAVING clause such as
SELECT [Id]
FROM [dbo].[TEST]
GROUP BY [Id]
HAVING SUM(CASE WHEN [ORGORDER] = 1 THEN 1 ELSE 0 END) = 0
where if there's at least one value equals to 1 for the concerned column([ORGORDER]), then that [Id] column won't be listed as result.
Demo

How to get the latest last 2 inserted records from the table

CREATE TABLE `testskm`(
`mem_id` NUMBER(5) NOT NULL,
`mem_sal` NUMBER(5) NOT NULL);
insert into `testskm` values (1,100);
insert into `testskm` values (1,200);
insert into `testskm` values (2,350);
insert into testskm values (2,150);
insert into testskm values (3,12);
insert into testskm values (1,300);
insert into testskm values (2,50);
insert into testskm values (3,13);
insert into testskm values (3,14);
insert into testskm values (3,15);
i have insert statements for mem_id 1,2, & 3. I want to get the last 2 inserted records in the table for all mem_id.
I have the tried the below code , but its giving me only records based on the mem_sal as i used the order by..
select * from(
select
mem_id, mem_sal,
--max(sysdate) over (partition by mem_id) latest,
rank() over( partition BY mem_id order by mem_sal ) RISK_ORDER
from testskm)
where RISK_ORDER <= 2
i want output of these inserted records:
insert into `testskm` values (1,200);
insert into `testskm` values (1,300);
insert into `testskm` values (2,150);
insert into `testskm` values (2,50);
insert into `testskm` values (3,14);
insert into `testskm` values (3,15);
SQL tables represent unordered sets. There is no "last two rows" unless a column specifies the ordering. Your table has no such column.
You can define one. In MySQL, this looks like:
create table `testskm` (
testskm_id int auto_increment primary key,
`mem_id` int NOT NULL,
`mem_sal` int NOT NULL
);
Then you can use row_number():
select ts.*
from (select ts.*, row_number() over (partition by mem_id order by testskm_id desc) as seqnum
from testskm ts
) ts
where seqnum <= 2;
Here is a db<>fiddle.

how to pick top 2 rows in a table based on the indicator

I have a sample data like this
Declare #table Table
(
ID INT,
Value VARCHAR(10),
Is_failure int
)
insert into #table(ID, Value, Is_failure) values (1, 'Bits', 0)
insert into #table(ID, Value, Is_failure) values (2, 'Ip', 0)
insert into #table(ID, Value, Is_failure) values (3, 'DNA', 0)
insert into #table(ID, Value, Is_failure) values (6, 'DCP', 1)
insert into #table(ID, Value, Is_failure) values (8, 'Bits', 0)
insert into #table(ID, Value, Is_failure) values (11, 'calc', 0)
insert into #table(ID, Value, Is_failure) values (14, 'DISC', 0)
insert into #table(ID, Value, Is_failure) values (19, 'DHCP', 1)
Looks like this:
ID Value Is_failure
1 Bits 0
2 Ip 0
3 DNA 0
6 DCP 1
8 Bits 0
11 calc 0
14 DISC 0
19 DHCP 1
Data continuous like this ... I need to fetch top 2 records along with Is_failure whenever Is_failure = 1 comes if it is 0 no need to pick up .
Sample output:
ID Value Is_failure
2 Ip 0
3 DNA 0
6 DCP 1
11 calc 0
14 DISC 0
19 DHCP 1
Suggest on this I have tried with having count(*) and other things but not fruitful.
You can use this query
Declare #tmptable Table
(
ID INT,
Value VARCHAR(10),
Is_failure int,
rowNum int
)
Declare #continuousRows int =2
insert into #tmptable
select *,ROW_NUMBER() over (order by id) from #table
;with cte1 as
(select *
from #tmptable t
where (select sum(Is_failure) from #tmptable t1 where t1.rowNum between t.rowNum-#continuousRows and t.rowNum
having count(*)=#continuousRows+1)=1
and t.Is_failure=1
)
,cte2 as
(
select t.* from #tmptable t
join cte1 c on t.rowNum between c.rowNum-#continuousRows and c.rowNum
)
select c.ID,value,Is_failure from cte2 c
You can use window functions for this:
select id, value, is_failure
from (select t.*,
lead(Is_failure) over (order by id) as next_if,
lead(Is_failure, 2) over (order by id) as next_if2
from #table t
) t
where 1 in (Is_failure, next_if, next_if2)
order by id;
You can simplify this with a windowing clause:
select id, value, is_failure
from (select t.*,
max(is_failure) over (order by id rows between current row and 2 following) as has_failure
from #table t
) t
where has_failure > 0
order by id;

Table transformation logic in SQL

I have a table T :
CREATE TABLE T
(
id INT,
type VARCHAR(200),
type_value VARCHAR(10),
value VARCHAR(200)
);
INSERT INTO T VALUES (1, 'RoomColor', 'room1', 'yellow');
INSERT INTO T VALUES (1, 'RoomColor', 'room2', 'red');
INSERT INTO T VALUES (2, 'RoomColor', 'room1', 'blue');
INSERT INTO T VALUES (2, 'RoomColor', 'room1', 'pink');
INSERT INTO T VALUES (3, 'RoomColor', 'room1', 'white');
INSERT INTO T VALUES (3, 'RoomColor', 'room2', 'grey');
INSERT INTO T VALUES (3, 'RoomColor', 'room2', 'brown');
INSERT INTO T VALUES (4, 'RoomColor', 'room3', 'green');
I need to transform it into :
id BedRoomColor DiningRoomColor
-------------------------------------------
1 yellow red
2 blue pink
3 white grey
4 green null
Logic behind the transformation:
If there are more than two room type_value then discard the third room type_value
For same id if there are more than one room type_value ( for example room1,room1 or room2,room2 or room1,room2) then use first type_value to create as BedRoomColor and second type_value to create DiningRoomColor
If there is only 1 room type_value (for eg. room1 or room2 or room3) for an id then corresponding value ( red,green,yellow etc ) will be placed in BedRoomColor and DiningRoomColor will be null
I am struggling with this logic for couple of days. Can anyone please help me.
Thanks
You can use this script
;WITH CTE AS (
SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY id ORDER BY type_value) FROM T
)
SELECT id, [1] BedRoomColor, [2] DiningRoomColor FROM
(SELECT id,value, RN FROM CTE ) SRC
PIVOT (MAX(value) FOR RN IN ([1], [2]) ) AS PVT
Result:
id BedRoomColor DiningRoomColor
----------- ------------------ ---------------
1 yellow red
2 blue pink
3 white grey
4 green NULL
Another way and with adding type to query is:
;with tt as (
select *,
row_number() over (partition by [type], id order by type_value) rn
-- ^^^^^^ I add type to support other types if there is
from t
)
select id,
max(case when [type] = 'RoomColor' and rn = 1 then [value] end) 'BedRoomColor',
max(case when [type] = 'RoomColor' and rn = 2 then [value] end) 'DiningRoomColor'
from tt
group by id;
SQL Server Fiddle Demo
try this:
with tmp as (
select T.*, rownumber() over(patition by id order by type_value) rang
from T
)
select f1.id, f1.value as BedRoomColor, f2.value as DiningRoomColor
from tmp f1
left outer join tmp f2 on f1.id=f2.id and f2.rang=2
where f1.rang=1