How to select a value on the first row and last on SQL? - sql

I have a table like this:
ID | persid | value1 | value2
---+--------+---------+---------
1 | A | string1 | string2
2 | A | string3 | string4
3 | A | string5 | string6
I need to return something like this using GROUP BY:
Field1 | field2 | field3
-------+---------+----------
A | string1 | string6
I'm using SQL server 2008,
Is this possible?

I would use conditional aggregation:
select persid,
max(case when seqnum_asc = 1 then value1 end) as value1,
max(case when seqnum_desc = 1 then value2 end) as value2
from (select t.*,
row_number() over (partition by persid order by id) as seqnum_asc,
row_number() over (partition by persid order by id desc) as seqnum_desc
from t
) t
group by persid;

You can try something like this:
create table tbl (id int, persid varchar(10), value1 varchar(10), value2 varchar(10));
insert into tbl (id, persid, value1, value2) values (1, 'A', 'string1', 'string2');
insert into tbl (id, persid, value1, value2) values (2, 'A', 'string3', 'string4');
insert into tbl (id, persid, value1, value2) values (3, 'A', 'string5', 'string6');
select tbl.persid, tbl.value1, persmax.value2
from tbl
inner join tbl persmax on persmax.id = (select max(id) from tbl where persid='A')
where tbl.persid='A'
and tbl.id = (select min(id) from tbl where persid='A')

I think you can use a query like this:
select top(1) yourTable.persid field1, yourTable.value1 field2, t.value2 field3
from yourTable
cross join (
select top(1) value2
from yourTable
order by ID desc
) t
order by ID;

How about this?
select distinct a, b, c from
(select mytbl.persid as a, mytbl.value1 as b from mytbl,
(select persid, min(id) as id from mytbl group by persid) as t1
where mytbl.id = t1.id and mytbl.persid = t1.persid) as t2,
(select mytbl.persid as d, mytbl.value2 as c from mytbl,
(select persid, max(id) as id from mytbl group by persid) as t3
where mytbl.id = t3.id and mytbl.persid = t3.persid) as t4
where t2.a = t4.d

Related

SQL - Update Rows from a list of values

So Table Setup is:
Column1 Column2 Column3
A 1 Null
B 2 Null
C 1 Null
D 2 Null
E 1 Null
F 2 Null
G 1 Null
H 2 Null
I would like to update Column3 with an array of values (Value1, Value2, Value3) and cycle through that list until the update is complete
The ultimate goal is for the table to look like this:
Column1 Column2 Column3
A 1 Value1
B 2 Value2
C 1 Value3
D 2 Value1
E 1 Value2
F 2 Value3
G 1 Value1
H 2 Value2
I originally tried in powershell but it was not working as I would have liked because of how the data is being imported, so now I am looking towards SQL. Any suggestions would be great!
You could try an update join here. The approach below is to assign an ordered sequence to both your original table and the "array" of values for updating. We join using modulus logic, such that your table's sequence ordering will match up the values in the array and will wrap around until all values have been assigned.
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY Column1) rn
FROM yourTable
)
UPDATE t1
SET Column3 = t2.val
FROM cte t1
INNER JOIN
(
SELECT 1 AS id, 'Value1' AS val UNION ALL
SELECT 2, 'Value2' UNION ALL
SELECT 3, 'Value3'
) t2
ON t2.id = 1 + ((t1.rn - 1) % 3);
Demo
Assuming you can put the "array" in a table, you can use something like this:
with vals as (
select v.*,
row_number() over (order by (select null)) - 1 as seqnum,
count(*) over () as cnt
from (values ('Value1'), ('Value2'), ('Value3')) v(val)
)
update t
set t.column3 = v.val
from (select t.*,
row_number() over (order by column1) - 1 as seqnum
from t
) t join
vals v
on t.seqnum % v.cnt = v.seqnum;
The basic idea is to enumerate the rows in each table and then use modulo arithmetic to match them.

Is it possible to find (in an ordered table) multiple rows in sequence? [duplicate]

This question already has an answer here:
Compare Current Row with Previous/Next row in SQL Server
(1 answer)
Closed 4 years ago.
If I have a table ordered by ID like so:
|---------------------|------------------|
| ID | Key |
|---------------------|------------------|
| 1 | Foo |
|---------------------|------------------|
| 2 | Bar |
|---------------------|------------------|
| 3 | Test |
|---------------------|------------------|
| 4 | Test |
|---------------------|------------------|
Is there a way to detect two rows that match a where clause in sequence?
For example, in the table above, I would like to see if any two rows in succession have a Key of 'test'.
Is this possible in SQL?
Another option is a variation of Gaps-and-Islands
Example
Declare #YourTable Table ([ID] int,[Key] varchar(50))
Insert Into #YourTable Values
(1,'Foo')
,(2,'Bar')
,(3,'Test')
,(4,'Test')
Select ID_R1 = min(ID)
,ID_R2 = max(ID)
,[Key]
From (
Select *
,Grp = ID-Row_Number() over(Partition By [Key] Order by ID)
From #YourTable
) A
Group By [Key],Grp
Having count(*)>1
Returns
ID_R1 ID_R2 Key
3 4 Test
EDIT - Just in case the IDs are NOT Sequential
Select ID_R1 = min(ID)
,ID_R2 = max(ID)
,[Key]
From (
Select *
,Grp = Row_Number() over(Order by ID)
-Row_Number() over(Partition By [Key] Order by ID)
From #YourTable
) A
Group By [key],Grp
Having count(*)>1
You can try to use ROW_NUMBER window function check the gap.
SELECT [Key]
FROM (
SELECT *,ROW_NUMBER() OVER(ORDER BY ID) -
ROW_NUMBER() OVER(PARTITION BY [Key] ORDER BY ID) grp
FROM T
)t1
GROUP BY [Key]
HAVING COUNT(grp) = 2
You can do a self join as
CREATE TABLE T(
ID INT,
[Key] VARCHAR(45)
);
INSERT INTO T VALUES
(1, 'Foo'),
(2, 'Bar'),
(3, 'Test'),
(4, 'Test');
SELECT MIN(T1.ID) One,
MAX(T2.ID) Two,
T1.[Key] OnKey
FROM T T1 JOIN T T2
ON T1.[Key] = T2.[Key]
AND
T1.ID <> T2.ID
GROUP BY T1.[Key];
Or a CROSS JOIN as
SELECT MIN(T1.ID) One,
MAX(T2.ID) Two,
T1.[Key] OnKey
FROM T T1 CROSS JOIN T T2
WHERE T1.[Key] = T2.[Key]
AND
T1.ID <> T2.ID
GROUP BY T1.[Key]
Demo
You can use the LEAD() window function, as in:
with
x as (
select
id, [key],
lead(id) over(order by id) as next_id,
lead([key]) over(order by id) as next_key
from my_table
)
select id, next_id from x where [key] = 'test' and next_key = 'test'

SQL - Two Columns into One Distinct Ordered Column

If I have a table like this:
Col 1 | Col 2
-------------
A | 1
A | 2
B | 1
C | 1
C | 2
C | 3
How can I write a query to pull one column that looks like this --
Col 1
------
A
1
2
B
1
C
1
2
3
SELECT col1
FROM Some_Table_You_Did_Not_Name
UNION ALL
SELECT col2
FROM Some_Table_You_Did_Not_Name
If the order matters in your example then you want this:
WITH data AS
(
SELECT col1, col2, ROW_NUMBER() OVER (ORDER BY col1, col2) as RN
FROM Some_Table_You_Did_Not_Name
)
SELECT col
FROM (
SELECT DISTINCT col1 as col, RN, 1 as O
FROM data
UNION ALL
SELECT DISTINCT col2 as col, RN, 2 as O
FROM data
) JC_IS_THAT_GUY
ORDER BY RN ASC, O ASC, col ASC
You can use a query like the following:
SELECT Col1
FROM (
SELECT DISTINCT Col1, Col1 AS Col2, 0 AS grp
FROM mytable
UNION ALL
SELECT Col2 AS Col1, Col1 AS Col2, 1 AS grp
FROM mytable) AS t
ORDER BY Col2, grp, Col1
Demo here
There is absolutely no need to do a UNION, UNION ALL or reference the table more than once to unpivot data...
-- if Col2 is always a well ordered sequense like the test data...
SELECT
Col1 = x.Value
FROM
#TestData td
CROSS APPLY ( VALUES (IIF(td.Col2 = 1, td.Col1, NULL)), (CAST(td.Col2 AS CHAR(1))) ) x (Value)
WHERE
x.Value IS NOT NULL;
-- if it isn't...
WITH
cre_Add_RN AS (
SELECT
td.Col1,
td.Col2,
RN = ROW_NUMBER() OVER (PARTITION BY td.Col1 ORDER BY td.Col2)
FROM
#TestData td
)
SELECT
x.Value
FROM
cre_Add_RN arn
CROSS APPLY ( VALUES (IIF(arn.RN = 1, arn.Col1, NULL)), (CAST(arn.Col2 AS CHAR(1))) ) x (Value)
WHERE
x.Value IS NOT NULL;
HTH,
Jason

How to find adjacent values in a column based on another column value in Sql

I have a table like below
From the above table I Need to select adjacent values from val3 column based on Number column
I Need output like below :
WITH T1
AS (SELECT *,
DENSE_RANK() OVER (PARTITION BY [Number] ORDER BY [Val3]) - [Val3] AS Grp
FROM YourTable),
T2
AS (SELECT *,
COUNT(*) OVER (PARTITION BY [Number], Grp) AS Cnt
FROM T1)
SELECT [Number],
[Val1],
[Val2],
[Val3]
FROM T2
WHERE Cnt > 1
An ugly solution would be:
select d.number, d.val1, d.val2, d.val3 from table d
where exists
(select * from table t
where t.number = d.number
--and t.val1 = d.val1
--and t.val2 = d.val2
and (t.val3 = d.val3 - 1 or t.val3 = d.val3 + 1))
You can identify a sequential set of numbers by the fact that the number - its position in the set will be constant, e.g.
Val3 | RowNumber | Val - RowNumber
4 | 1 | 3
5 | 2 | 3
6 | 3 | 3
8 | 4 | 4
9 | 5 | 4
As you can see the Val - RowNumber column remains constant for each sequential set. So to apply this to your query:
WITH GroupedT AS
( SELECT Number,
Val1,
Val2,
Val3,
GroupingSet = Val3 - DENSE_RANK() OVER(PARTITION BY Number, Val1, Val2 ORDER BY Val3)
FROM T
), SequentialT AS
( SELECT Number,
Val1,
Val2,
Val3,
SequenceCount = COUNT(*) OVER(PARTITION BY Number, Val1, Val2, GroupingSet)
FROM GroupedT
)
SELECT Number, Val1, Val2, val3
FROM SequentialT
WHERE SequenceCount > 1
ORDER BY Number, Val1, Val2, Val3;
Example on SQL Fiddle
It looks like what you're trying to do is eliminate any records that contain a Val3 value that doesn't have any adjacent Val3 values in other records. To do that, I think you can use something like this:
SELECT t1.*
FROM myTable t1
JOIN myTable t2 ON t1.Number = t2.Number
AND t1.Val1 = t2.Val1
AND t1.Val2 = t2.Val2
AND (t2.Val3 IN (t1.Val3 - 1, t1.Val3 + 1))

SQL Server: How to convert column into one row?

I have one column with different values depending on result.
I would like to return one row with fix columns.
The column output as follows:
Group | Item
--------------
G1 | 1
G1 | 2
G2 | 3
I would like to output as:
Group | Item1 | Item2 | Item3
-----------------------------
G1 | 1 | 2 | N/A
G2 | 3 | N/A | N/A
Please Help..
Thanks in Advance...
Because every group may have three items maximum, I would use PIVOT operator:
DECLARE #Test TABLE
(
RowID INT IDENTITY(1,1) PRIMARY KEY
,[Group]VARCHAR(10) NOT NULL
,Item INT NOT NULL
,UNIQUE ([Group], Item)
);
INSERT #Test VALUES ('G1', 1);
INSERT #Test VALUES ('G1', 2);
INSERT #Test VALUES ('G2', 3);
WITH PivotSource
AS
(
SELECT t.[Group], t.Item
,ROW_NUMBER() OVER(PARTITION BY t.[Group] ORDER BY t.RowID) RowNumber
FROM #Test t
)
SELECT pvt.[Group]
,Item1 = ISNULL( CONVERT(VARCHAR(11), pvt.[1]) , 'N/A')
,Item2 = ISNULL( CONVERT(VARCHAR(11), pvt.[2]) , 'N/A')
,Item3 = ISNULL( CONVERT(VARCHAR(11), pvt.[3]) , 'N/A')
FROM PivotSource src
PIVOT ( MAX(src.Item) FOR src.RowNumber IN ([1], [2], [3]) ) pvt;
Results:
Group Item1 Item2 Item3
---------- ----------- ----------- -----------
G1 1 2 N/A
G2 3 N/A N/A
I guess something like this will work:
SELECT t1.[Group], t1.Item, t2.Item, t3.Item
from tbl t1, tbl t2, tbl t3
where t1.[Group] = t2.[Group] and t1.[Group] = t3.[Group]
and t1.Item < t2.Item and t2.Item < t3.Item
and t1.[Group] in (select [Group] from tbl group by [Group] having COUNT(*) = 3)
union
SELECT t1.[Group], t1.Item, t2.Item, NULL
from tbl t1, tbl t2
where t1.[Group] = t2.[Group]
and t1.Item < t2.Item
and t1.[Group] in
(select [Group] from tbl group by [Group] having COUNT(*) = 2)
union
select tbl.[Group], tbl.Item, NULL, NULL
from tbl
where [Group] in (select [Group] from tbl group by [Group] having COUNT(*) = 1)
As you have a fixed number of output columns you can do some tricks with ROW_NUMBER() and over (partition by ... order by ...).
select child1.[Group], child1.Item as Item1, child2.Item as Item2, child3.Item as Item3 from
(select [Group], Item from
(select [Group], Item, (ROW_NUMBER() over(partition by [group] order by item)) as rownum from GroupTable) as child1_inner
where child1_inner.rownum = 1) as child1
left outer join
(select [Group], Item from
(select [Group], Item, (ROW_NUMBER() over(partition by [group] order by item)) as rownum from GroupTable) as child2_inner
where child2_inner.rownum = 2) as child2
on child1.[Group] = child2.[Group]
left outer join
(select [Group], Item from
(select [Group], Item, (ROW_NUMBER() over(partition by [group] order by item)) as rownum from GroupTable) as child3_inner
where child3_inner.rownum = 3) as child3
on child1.[Group] = child3.[Group]
This returns null instead of N/A, but you can fix that with COALESCE.