How to get the desired output using sql - sql

I have my DB table looks like the following
IDCol Col1 Col2 Col3
ID1 1 3 1000
ID1 6 6 1000
ID2 3 4 500
ID2 1 7 500
I need the output as the following
IDCol Col1 Col2 Col3
ID1 1 6 1000
ID2 3 7 500
Is there a possibility that I could get the above desired output format using SQL?
I am a newbie to SQL and could some one please help me? Thank you very much!

Based on your sample data, I believe this is what you're looking for:
SELECT IDCol
, MIN(Col1) AS [Col1]
, MAX(Col2) AS [Col2]
, Col3
FROM Table
GROUP BY IDCol
, Col3
Based on the information from your comments, if you add a row number column, then one of the solutions would be in the form of:
select t1.idcol
, t1.col1
, t2.col2
, t1.col3
from
(select idcol
, min(rn) over (partition by idcol) first_val
, max(rn) over (partition by idcol) last_val
from table
group by idcol, rn) r
inner join table t1 on r.first_val = t1.rn
and r.idcol = t1.idcol
inner join table t2 on r.last_val = t2.rn
and r.idcol = t2.idcol
group by t1.idcol
, t1.col1
, t2.col2
, t1.col3
Sample code running on your sample data:
declare #tbl table (rn int identity(1,1), idcol varchar(4), col1 int, col2 int, col3 int)
insert #tbl values ('id1', 1, 3, 1000)
, ('id1', 6, 6, 1000)
, ('id2', 3, 4, 500)
, ('id2', 1, 7, 500);
select t1.idcol
, t1.col1
, t2.col2
, t1.col3
from
(select idcol
, min(rn) over (partition by idcol) first_val
, max(rn) over (partition by idcol) last_val
from #tbl
group by idcol, rn) r
inner join #tbl t1 on r.first_val = t1.rn
and r.idcol = t1.idcol
inner join #tbl t2 on r.last_val = t2.rn
and r.idcol = t2.idcol
group by t1.idcol
, t1.col1
, t2.col2
, t1.col3

Another way of trying it using LEAD (supported from 2012):
declare #t1 table
(
IDCol char(5),
Col1 INT,
Col2 INT,
Col3 INT
)
Insert into #t1 values ('ID1', 3 , 3 , 1000);
Insert into #t1 values ('ID1', 6 , 6 , 1000);
Insert into #t1 values ('ID1', 6 , 4 , 1000);
Insert into #t1 values ('ID2', 11 , 3 , 1000);
Insert into #t1 values ('ID2', 6 , 6 , 1000);
Select * from
(
Select t1.IDCol, t1.Col1,
Lead(t1.Col2) Over (Partition by t1.IDCol Order By t1.IDCol) Col2,
t1.Col3 From
(
Select IDCol, Col1, Col2, Col3,
ROW_NUMBER() over(partition by IDCol order by IDCol) as RowFlg
from #t1
) t1,
(
Select IDCol, Min(RowFlg) Col1MinVal, Max(RowFlg) Col2MaxVal from
(
Select IDCol, ROW_NUMBER() over(partition by IDCol order by IDCol) as RowFlg from #t1
) z
Group By IDCol
) x
Where t1.IDCol = x.IDCol AND (t1.RowFlg = x.Col1MinVal OR t1.RowFlg = x.Col2MaxVal)
) main
Where Col2 is not null

I think you have to specify the level of abstraction for this task. You can achieve this output using many statements. The questions are what does the data rows represent and why the demanded output should be expected.

Related

Need to get the value from a column whose column name is based on a value in another table

Table A has columns ID, COL1, COL2, COL3.
Table B has columns AID, ColumnName.
I need to get the [ColumnName] value in Table A based on the value of [ColumnName] in Table B.
In the example below:
For ID 1, I need to get the value of column COL1 (This is the value of [ColumnName] for AID 1 in Table B).
For ID 2, I need to get the value of column COL3 (This is the value of [ColumnName] for AID 2 in Table B).
Table A
ID COL1 COL2 COL3
1 a aa aaa
2 b bb bbb
Table B
AID ColumnName
1 COL1
2 COL3
Desired Result:
ID VALUE
1 a
2 bbb
How can I do that ?
Thank you.
Unpivot then join
drop table t
go
drop table t1
go
create table t
(ID int, COL1 varchar(10), COL2 varchar(10), COL3 varchar(10))
go
create table t1
(AID int,ColumnName varchar(10));
go
insert into t values
(1 , 'a', 'aa', 'aaa'),
(2 , 'b', 'bb', 'bbb')
go
insert into t1 values
(1 , 'COL1'),
(2 , 'COL3')
go
with cte as
(select id, u.col, u.val
from t
unpivot
(
val
for col in (col1, col2, col3)
) u
)
select cte.id,cte.val
from cte
join t1 on
t1.aid = cte.id and
t1.columnname = cte.col
go
id val
----------- ----------
1 a
2 bbb
(2 row(s) affected)
One possible approach is to unpivot the columns in TableA using VALUES table value constructor and additional APPLY operator:
Tables:
SELECT *
INTO TableA
FROM (VALUES
(1, 'a', 'aa', 'aaa'),
(2, 'b', 'bb', 'bbb')
) v (ID, COL1, COL2, COL3)
SELECT *
INTO TableB
FROM (VALUES
(1, 'COL1'),
(2, 'COL3')
) v (AID, COL)
Statement:
SELECT b.AID, v.VALUE
FROM TableB b
JOIN TableA a ON b.AID = a.ID
CROSS APPLY (VALUES
('COL1', a.COL1),
('COL2', a.COL2),
('COL3', a.COL3)
) v (COL, [VALUE])
WHERE v.COL = b.COL
Result:
AID
VALUE
1
a
2
bbb

Not sure about the below scenario. Need a bit push. How can I solve below sql scenario

Input:
COL1 COL2
---------------
10 a
20 b
30 c
40 NULL
50 d
Desired output:
COL1 COL2
-----------------
10 a
20 a,b
30 a,b,c
40 a,b,c
50 a,b,c,d
Below is the solution I have tried so far. But this is not returning the desired output.
WITH CTE AS
(
SELECT
COL1,
LAG(COL2) OVER (ORDER BY COL1) AS prev_word,
COL2
FROM
dbo.Scenario
), CTE_A AS
(
SELECT
COL1, COL2, prev_word,
CONCAT(ISNULL(Prev_word, ''), ' ', ISNULL(COL2, '')) AS Con_Word
FROM
CTE
)
SELECT *
FROM CTE_A
One possible solution is the following statement. I assume, that the values in the COL1 column define the order, that is needed for the aggregation.
Table:
CREATE TABLE Data (
COL1 int,
COL2 varchar(1)
)
INSERT INTO Data (COL1, COL2)
VALUES
(10, 'a'),
(20, 'b'),
(30, 'c'),
(40, NULL),
(50, 'd')
Statement for SQL Server 2012:
SELECT d.COL1, STUFF(a.COL2, 1, 1, '') AS COL2
FROM Data d
CROSS APPLY (
SELECT CONCAT(',', COL2)
FROM Data
WHERE COL1 <= d.COL1 AND COL2 IS NOT NULL
ORDER BY COL2
FOR XML PATH('')
) a (COL2)
ORDER BY d.COL1
Statement for SQL Server 2017+ (using STRING_AGG() for string aggregation):
SELECT d1.COL1, STRING_AGG(d2.COL2, ',') WITHIN GROUP (ORDER BY d2.COL1) AS COL2
FROM Data d1
JOIN Data d2 ON d1.COL1 >= d2.COL1
WHERE d2.COL2 IS NOT NULL
GROUP BY d1.COL1
ORDER BY d1.COL1
Result:
COL1 COL2
10 a
20 a,b
30 a,b,c
40 a,b,c
50 a,b,c,d
try the following:
declare #t table (COL1 int, COL2 varchar(max))
insert into #t select 10, 'a'
insert into #t select 20, 'b'
insert into #t select 30, 'c'
insert into #t select 40, NULL
insert into #t select 50, 'd'
select COL1, STUFF(
(
SELECT DISTINCT ',' + COL2 FROM #t t2
WHERE t.COL1 >= t2.COL1 for xml path('')
),1,1,''
) AS COL2
from #t t
SELECT ID,STUFF((SELECT DISTINCT ',' + [Values] FROM Table_ t2
WHERE t.ID>= t2.ID for xml path('')),1,1,'') AS [Values]
FROM Table_ t

Update a target table only when source data are not null and different

I have 2 tables with the same columns. The first table Temp1 is loaded with a sp and is temporary. It is used to load a table T1 on a production environment. By example, I have this data :
Table Temp1
Id Col1 Col2 Col3 Col4 Col5
2 null null 0.5 null 0.6
3 0.1 null null null null
Table T1
Id Col1 Col2 Col3 Col4 Col5
1 2 3 0.4 5 0.6
2 5 4 6 4 7
3 8 9 7 10 1
I need to update T1 with data from Temp1 only when columns from Temp1 are not null and have not the same value in T1 (bold values in my example).
I’m stuck with this problem. Any ideas please ?
Thanks,
Using an update statement with a case expression would be something like this.
update tt1
set Col1 = case when T.Col1 is not null and T.Col1 <> tt1.Col1 then T.Col1 else tt1.Col1 end
from T1 tt1
join Temp1 t on t.Id = tt1.Id
UPDATE T1
SET T1.Col1 = ISNULL(NULLIF(ISNULL(tmp.Col1 , T1.Col1) , T1.Col1) , T1.Col1)
, T1.Col2 = ISNULL(NULLIF(ISNULL(tmp.Col2 , T1.Col2) , T1.Col2) , T1.Col2)
, T1.Col3 = ISNULL(NULLIF(ISNULL(tmp.Col3 , T1.Col3) , T1.Col3) , T1.Col3)
, T1.Col4 = ISNULL(NULLIF(ISNULL(tmp.Col4 , T1.Col4) , T1.Col4) , T1.Col4)
, T1.Col5 = ISNULL(NULLIF(ISNULL(tmp.Col5 , T1.Col5) , T1.Col5) , T1.Col5)
FROM Temp1 tmp
INNER JOIN T1 ON t1.ID = tmp.ID
you can use a merge statement for this problem.
CREATE TABLE #t
(
ID int IDENTITY(1,1),
Name NVARCHAR(50),
Salary DECIMAL(10,2)
)
INSERT INTO #t (Name, Salary) VALUES('abc', 123.4)
INSERT INTO #t (Name, Salary) VALUES('pqa', 127.4)
INSERT INTO #t (Name, Salary) VALUES('xyz', 233.4)
CREATE TABLE #tempForT
(
ID int IDENTITY(1,1),
Name NVARCHAR(50),
Salary DECIMAL(10,2)
)
INSERT INTO #tempForT (Name, Salary) VALUES('abc', 200.4)
INSERT INTO #tempForT (Name, Salary) VALUES('pqa', 200.4)
INSERT INTO #tempForT (Name, Salary) VALUES('xyz', NULL)
SELECT * FROM #t
SELECT * FROM #tempForT
Here is the Solution
MERGE #t AS DestTable
USING (
SELECT
NAME,
Salary
FROM #tempForT
)SourceTable
ON DestTable.Name = SourceTable.Name --Should contain columns like composite primary key
--AND other columns
WHEN MATCHED
THEN
UPDATE
SET DestTable.Salary = ISNULL(SourceTable.Salary, DestTable.Salary) --checking for NULL
;
SELECT * FROM #t
Note: If #tempForT contains new values (rows), then those rows will not get inserted for that you have to update the merge statement with WHEN NOT MATCHED in that case insert new records.

find max value in a row and update new column with the max column name

I have a table like this
number col1 col2 col3 col4 max
---------------------------------------
0 200 150 300 80
16 68 250 null 55
I want to find max value between col1,col2,col3,col4 in every row and update the last column "max" with the max value column name!
for example in first row max value is 300 the "max" column value will be "col3"
result like this:
number col1 col2 col3 col4 max
------------------------------------------
0 200 150 300 80 col3
16 68 250 null 55 col2
How can I do this?
QUERY
SELECT *,(
SELECT MAX(n)
FROM
(
VALUES(col1),(col2),(col3),(col4)
) AS t(n)
) AS maximum_value
FROM #tmp
Update statement
with MaxValues
as (select [number], [max] = (
select (
select max ([n])
from (values ([col1]) , ([col2]) , ([col3]) , ([col4])) as [t] ([n])
) as [maximum_value])
from [#tmpTable])
update [#tmpTable]
set [max] = [mv].[max]
from [MaxValues] [mv]
join [#tmpTable] on [mv].[number] = [#tmpTable].[number];
assuming number is a key column
SQL Fiddle
Check in SQL Fiddle
Schema
DECLARE #temp table ([number] int NOT NULL, [col1] int, [col2] int, [col3] int, [col4] int, [colmax] int);
INSERT #temp VALUES (0, 200, 150, 300, 80, null), (16, 68, 250, null, 55, null);
Query
SELECT number
,(
SELECT MAX(col) maxCol
FROM (
SELECT t.col1 AS col
UNION
SELECT t.col2
UNION
SELECT t.col3
UNION
SELECT t.col4
) a
) col
FROM #temp t
and the update statement is -
UPDATE tempCol
SET colmax = a.col
FROM (
SELECT (
SELECT MAX(col) maxCol
FROM (
SELECT t.col1 AS col
UNION
SELECT t.col2
UNION
SELECT t.col3
UNION
SELECT t.col4
) a
) col
FROM tempCol t
) a

interesting t-sql exercise

I am trying to resolve t-sql exercise
I need to update first table with values from second by joining by id. If I can not join then use value from default ID (default iD is the Id that is null)
please run it to see it
declare #t as table (
[id] INT
,val int
)
insert into #t values (null, null)
insert into #t values (2, null)
insert into #t values (3, null)
insert into #t values (4, null)
declare #t2 as table (
[id] INT
,val int
)
insert into #t2 values (null, 11)
insert into #t2 values (2, 22)
insert into #t2 values (3, 33)
select * from #t
select * from #t2
update t
set t.val = t2.val
from #t as t join #t2 as t2
on t.id = t2.id
or
(
(t.id is null or t.id not in (select id from #t2))
and t2.id is null
)
select * from #t
here is result
--#t
id val
---------------
NULL NULL
2 NULL
3 NULL
4 NULL
--#t2
id val
---------------
NULL 11
2 22
3 33
--#t after update
id val
---------------
NULL 11
2 22
3 33
4 NULL
how to make val in last row equal 11?
4 11
This solution left joins to t2 twice and then does a coalesce.
The ON on the second join matches on records that failed on the join and then looks for the "Default" case.
UPDATE t
set t.val = COALESCE(t2.val,t3.val)
from #t as t
LEFT join #t2 as t2
on t.id = t2.id
LEFT JOIN #t2 t3
ON t2.id is null and t3.id is null
See it working here
try this for the update...
update t
set t.val = t2.val
from #t as t join #t2 as t2
on t.id = t2.id
or
(
(t.id is null or not exists (select * from #t2 where id = t.id))
and t2.id is null
)
Problem is with not in operator and null values. This would also work...
update t
set t.val = t2.val
from #t as t join #t2 as t2
on t.id = t2.id
or
(
(t.id is null or t.id not in (select id from #t2 where id is not null))
and t2.id is null
)
Here's a technique that may help.
Start with the kind of simple code you want to be writing:
MERGE INTO #t AS target
USING source
ON target.id = source.id
WHEN MATCHED THEN
UPDATE
SET val = source.val;
Then write a table expression (source) that satisfies the requirements.
Requirement 1: "joining by id"
-- simple existential quantification e.g.
SELECT id, val
FROM #t2 AS T2
WHERE id IN ( SELECT id FROM #t )
Requirement 2: "If I can not join then use value from default ID (default iD is the Id that is null)"
-- first find the id values in the target that do not exist in the source:
SELECT id
FROM #t
EXCEPT
SELECT id
FROM #t2
then cross join the result with the row from the source where Id is null:
SELECT DT1.id, T2.val
FROM ( SELECT id
FROM #t
EXCEPT
SELECT id
FROM #t2 ) AS DT1,
#t2 AS T2
WHERE T2.id IS NULL
At this point you will want to query some test data to ensure each query satisfies its respective requirement.
Union the above two results to form a single table expression:
SELECT id, val
FROM #t2 AS T2
WHERE id IN ( SELECT id
FROM #t )
UNION
SELECT DT1.id, T2.val
FROM ( SELECT id
FROM #t
EXCEPT
SELECT id
FROM #t2 ) AS DT1,
#t2 AS T2
WHERE T2.id IS NULL
Then plug the table expression into the MERGE boilerplate code:
WITH source
AS
(
SELECT id, val
FROM #t2 AS T2
WHERE id IN ( SELECT id
FROM #t )
UNION
SELECT DT1.id, T2.val
FROM ( SELECT id
FROM #t
EXCEPT
SELECT id
FROM #t2 ) AS DT1,
#t2 AS T2
WHERE T2.id IS NULL
)
MERGE INTO #t AS target
USING source
ON target.id = source.id
WHEN MATCHED THEN
UPDATE
SET val = source.val;