Merge rows into one row based on condition - sql

I Have a table structure which similar to below
Create Table #Temp(Name varchar(10),Col1 int,Col2 int,Col3 int,Col4 int,Col5 int)
In this case table can have same name repeated but the other values to be different
So.
Sample values can be like
Insert Into #Temp
Values('ABC',1,0,0,1,1)
Insert Into #Temp
Values('ABC',1,0,1,1,0)
Insert Into #Temp
Values('ABC',1,0,1,1,0)
Insert Into #Temp
Values('DEF',0,0,0,1,0)
Insert Into #Temp
Values('DEF',1,0,1,1,1)
Insert Into #Temp
Values('DEF',1,1,0,1,1)
What I am trying to do here is select only one row for each name, but select the column with priority where it has value 1.
So the expected result in this case is
Name Col1 Col2 Col3 Col4 Col5
ABC 1 0 1 1 1
DEF 1 1 1 1 1
I have achieved it by doing something like below, which works absolutely fine. But is there any proper(easy) way of doing this.
SELECT Name,
(Select top 1 Col1
from #Temp T
Where T.Name=M.Name
Order By Col1 desc) as Col1,
(Select top 1 Col2
from #Temp T
Where T.Name=M.Name
Order By Col2 desc) as Col2,
(Select top 1 Col3
from #Temp T
Where T.Name=M.Name
Order By Col3 desc) as Col3,
(Select top 1 Col4
from #Temp T
Where T.Name=M.Name
Order By Col4 desc)as Col4,
(Select top 1 Col5
from #Temp T
Where T.Name=M.Name
Order By Col5 desc) as Col5
FROM #Temp M
Group By Name

It seems to me that you need to use MAX:
SELECT [Name],
MAX(Col1) Col1,
MAX(Col2) Col2,
MAX(Col3) Col3,
MAX(Col4) Col4,
MAX(Col5) Col5
FROM #Temp
GROUP BY [Name]

SELECT
Name,
Max(Col1) as Col1,
Max(Col2) as Col2,
Max(Col3) as Col3,
Max(Col4) as Col4,
Max(Col5) as Col5
FROM #Temp
GROUP BY Name

Related

delete a row after comparing specific values with other rows in the same table

I have a table like this ( SQL SERVER )
col1 col2 col3 col4 col5
1 Goerge A B ++
2 Alex B B aa
2 Alex B B ++
now the second and the third rows have almost same values except in col5. in this case i want to delete one of them ( i want to be able to decide wich row from both of them i wanna delete ) like the one with value ++ so the table should look like this :
col1 col2 col3 col4 col5
1 Goerge A B ++
2 Alex B B aa
How can that be achieved?
Use CTE with ROW_NUMBER() to find (and delete) your duplicates
WITH CTE_Dup AS
(
SELECT *
, ROW_NUMBER OVER() PARTITION BY (Col1, Col2, Col3, Col4 ORDER BY Col5) RN
-- change ORDER BY criteria to make sure row you want to keep is 1st
FROM YourTable
)
DELETE --Try SELECT * to check before actually deleting
FROM CTE_Dup WHERE RN>1
It is not at all clear the logic you want here for determining which row is a "duplicate" but this example should show you a pretty straight forward way of handling this kind of thing.
declare #Something table
(
col1 int
, col2 varchar(10)
, col3 char(1)
, col4 char(1)
, col5 char(2)
)
insert #Something
values
(1, 'Goerge', 'A', 'B', '++')
, (2, 'Alex', 'B', 'B', 'aa')
, (2, 'Alex', 'B', 'B', '++')
;
with SortedValues as
(
select *
, ROW_NUMBER() over (partition by col1 order by col5 desc) as RowNum
from #Something
)
delete SortedValues
where RowNum > 1
select *
from #Something
You can try the below query. I grouped the columns and get the count of that and handled the conditions in the WHERE clause.
Query execution with the given sample data:
DECLARE #TestTable TABLE (col1 INT, col2 VARCHAR (20), col3 VARCHAR (20), col4 VARCHAR (20), col5 VARCHAR (20));
INSERT INTO #TestTable (col1, col2, col3, col4, col5) VALUES
(1, 'Goerge', 'A', 'B', '++'),
(2, 'Alex', 'B', 'B', 'aa'),
(2, 'Alex', 'B', 'B', '++');
SELECT * INTO #tmpResults FROM (
SELECT col1, col2, col3, col4, col5,
COUNT(*) OVER (PARTITION BY col1, col2, col3, col4 ORDER BY col1 ) AS CCnt
FROM #TestTable
) a
SELECT col1, col2, col3, col4, col5 FROM #tmpResults WHERE Ccnt = 1 AND Col5 = '++'
UNION
SELECT col1, col2, col3, col4, col5 FROM #tmpResults WHERE Ccnt > 1 AND Col5 <> '++';
DROP TABLE #tmpResults
Output:
col1 col2 col3 col4 col5
------------------------------------
1 Goerge A B ++
2 Alex B B aa

Sql insert select from – multiple rows with unique column id

I am trying to copy multiple records using one query using insert select from.
Insert into tab_A(colId, col1, col2, col3)
Select colId, col1, col2, col3 form tab_A
Where colId in ( 2,4,6)
Would it be possible to assign different colId for new entries? For example colid 2 should be replaced with 23, 4 with 24 and 6 with 25. How could I achieve it in a single query?
this would work
Insert into tab_A(colId, col1, col2, col3)
Select 23 , col1, col2, col3 form tab_A Where colId = 2 UNION ALL
Select 24 , col1, col2, col3 form tab_A Where colId = 4 UNION ALL
Select 25 , col1, col2, col3 form tab_A Where colId = 6
If you give some more info I could provide somthing more reusable. Should/is colId (be) an identity column?
EDIT
This would work in this very specialised case
Insert into tab_A(colId, col1, col2, col3)
Select ((colId - 4) * (-1)) + colId + 20 , col1, col2, col3
form tab_A Where colId IN (2, 4, 6)
The function newId = ((oldId - 4) * (-1)) + oldId + 20 is obviously specific to the stated problem.
EDIT2
I suspect somthing like this is more generic approach is appropriate.
DECLARE #MaxColID INT
BEGIN TRANSACTION
SELECT #MaxColID = MAX(ColID) FROM tab_A
INSERT tab_A(colId, col1, col2, col3)
SELECT row + #MaxColID, col1, col2, col3
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY ColID) row, col1, col2, col3
FROM tab_A WHERE colID IN (2, 4, 6)
)
COMMIT
EDIT 3
If you think EDIT 2 is actually what you want then you really want to make ColID an IDENTITY column, then you could do this.
INSERT tab_A (col1, col2, col3)
SELECT col1, col2, col3 FROM tab_A WHERE colId IN (2, 4, 6)
I dont see col4 or col6 in your query, but is this what you want:
Insert into tab_A(colId, col1, col2, col3)
Select colId, col1, 23, col3 form tab_A
Where colId in ( 2,4,6)
have you just tried adding the disired difference to colId -
In your case, since you need to replace 2 by 23, difference is 21.
Insert into tab_A(colId, col1, col2, col3)
Select colId+21, col1, col2, col3
form tab_A Where colId in ( 2,4,6)
Note: I missed the part, that the differnce is not consistent in your case.
The proposed solution will work only if difference is same
There are a few options:
Add the new ID column to the original table and populate it with the new values before you do this insert, selecting the new ID column instead of the old. This would be the tidiest solution I think.
Alternative - Modify the ID value on the insert based on a rule e.g.
INSERT INTO tab_A(colID, col1, col2, col3)
SELECT colId + 20, col1, col2, col3
FROM tab_A
WHERE colID IN(2,4,6)
Last resort - Process the insert sequentially with a cursor, modifying the ID value each time.
You could also write case in the select. when 2 then 23 or whatever value.

SQL No Dup (Distinct) on rows, but ignore one col

For Example, Table with three cols and data
col1 col2 col3
10 20 30
40 50 60
40 50 80
Want to do a select where for the last two rows only returns one since col1 and col2 are the same.
select distinct will not work since col3 are different.
so output would be
10 20 30
40 50 xx (don't care)
As you only have one additional column you can just use an arbitrary MIN/MAX aggregate and GROUP BY
SELECT col1,
col2,
MAX(col3) AS col3
FROM YourTable
GROUP BY col1,
col2
More generally if your RDBMS supports analytic functions you can use
WITH T
AS (SELECT col1,
col2,
col3,
ROW_NUMBER() OVER (PARTITION BY col1, col2
ORDER BY col1, col2) AS RN
FROM YourTable)
SELECT col1,
col2,
col3
FROM T
WHERE RN = 1
I did this in SQL Server:
-- Setup test data:
declare #table table (
col1 int,
col2 int,
col3 int
)
insert into #table values (10, 20, 30)
insert into #table values (40, 50, 60)
insert into #table values (40, 50, 80)
-- Here's the query:
select col1, col2, cast(min(col3) as varchar(10)) as col3
from #table
group by col1, col2
having count(*) = 1
union all
select col1, col2, 'xx' as col3
from #table
group by col1, col2
having count(*) > 1
I suppose this assumes that you have no duplicate rows (where all fields are duplicates), otherwise you'd have a possibly incorrect 'xx'.

SQL query to transfer values from rows to columns for subselect groups

Here's the problem:
The dataset is:
Col1 Col2
BK.01.04 A0103
BK.01.04 A0306
BK.01.04 A0309
BK.01.04 A0403
BK.02.01 A1403
BK.02.02 A1403
BK.02.03 A0403
BK.02.03 A0703
BK.02.04 A0103
BK.02.04 A0306
BK.02.04 A0309
BK.02.04 A0403
The required result is:
Col1 Col2 Col3 Col4 Col5
BK.01.04 A0103 A0306 A0309 A0403
BK.02.01 A1403
BK.02.02 A1403
BK.02.04 A0103 A0306 A0309 A0403
Any ideas on how to do this in MS Access/Plain SQL? Any help is greatly appreciated :)
Join the table back to itself:
select
t1.col1,
t1.col2,
t2.col2 as col3,
t3.col2 as col4,
t4.col2 as col5
from mytable t1
left join mytable t2 on t2.col1 = t1.col1 and t2.col2 > t1.col2
left join mytable t3 on t3.col1 = t1.col1 and t3.col2 > t2.col2 and t2.col2 is not null
left join mytable t4 on t4.col1 = t1.col1 and t4.col2 > t3.col2 and t3.col2 is not null
order by 1;
Using left join means you'll get blanks when there aren't matching rows in the joined table. All that crap in the join conditions is to make sure there's a different and ascending value in each column
Not quite exactly what you wanted, but I think you'll find it challenging to have the leftmost column populated with the value you want...
A pivot table using ANSI-SQL:
CREATE TABLE DataSet ( Col1 varchar(20), Col2 varchar(20))
INSERT INTO DataSet ( Col1, Col2 ) VALUES ( 'BK.01.04','A0103')
INSERT INTO DataSet ( Col1, Col2 ) VALUES ( 'BK.01.04','A0306')
INSERT INTO DataSet ( Col1, Col2 ) VALUES ( 'BK.01.04','A0309')
INSERT INTO DataSet ( Col1, Col2 ) VALUES ( 'BK.01.04','A0403')
INSERT INTO DataSet ( Col1, Col2 ) VALUES ( 'BK.02.01','A1403')
INSERT INTO DataSet ( Col1, Col2 ) VALUES ( 'BK.02.02','A1403')
INSERT INTO DataSet ( Col1, Col2 ) VALUES ( 'BK.02.03','A0403')
INSERT INTO DataSet ( Col1, Col2 ) VALUES ( 'BK.02.03','A0703')
INSERT INTO DataSet ( Col1, Col2 ) VALUES ( 'BK.02.04','A0103')
INSERT INTO DataSet ( Col1, Col2 ) VALUES ( 'BK.02.04','A0306')
INSERT INTO DataSet ( Col1, Col2 ) VALUES ( 'BK.02.04','A0309')
INSERT INTO DataSet ( Col1, Col2 ) VALUES ( 'BK.02.04','A0403')
SELECT Col1,
MAX(CASE Col2 WHEN 'A0103' THEN 'A0103' ELSE '' END) Col2,
MAX(CASE Col2 WHEN 'A0306' THEN 'A0306' ELSE '' END) Col3,
MAX(CASE Col2 WHEN 'A0309' THEN 'A0309' ELSE '' END) Col4,
MAX(CASE Col2 WHEN 'A0403' THEN 'A0403' ELSE '' END) Col5,
MAX(CASE Col2 WHEN 'A1403' THEN 'A1403' ELSE '' END) Col6
FROM DataSet
GROUP BY DataSet.Col1
Results Are:
Col1 Col2 Col3 Col4 Col5 Col6
-------------------- ----- ----- ----- ----- -----
BK.01.04 A0103 A0306 A0309 A0403
BK.02.01 A1403
BK.02.02 A1403
BK.02.03 A0403
BK.02.04 A0103 A0306 A0309 A0403
(5 row(s) affected)
If you're using MS-Access, you can accomplish the same thing by using pivot-tables:
http://office.microsoft.com/en-us/access-help/designing-your-first-pivottable-pivotchart-views-in-access-HA001034580.aspx

Switching 1 row with few columns into 1 column with few rows in MS SQL SERVER

i've got 1 row with many columns:
col1|col2|col3|...
and i want to have 1 column with many rows, like that:
col1
col2
col3
..
UNPIVOT if you're using version 2005+
http://www.tsqltutorials.com/unpivot.php
If SQL Server 2000 or lower...
How to rotate a table in SQL Server: http://support.microsoft.com/kb/175574
Take a look at UNPIVOT:
CREATE TABLE Table1 (col1 INT, col2 INT, col3 INT, col4 INT);
INSERT INTO Table1 (col1, col2, col3, col4) VALUES (1,2,3,4);
SELECT col, value FROM Table1
UNPIVOT (value FOR col IN (col1, col2, col3, col4)) AS unpvt
Result:
col value
col1 1
col2 2
col3 3
col4 4
If you don't want to know which value came from which column, only select value:
SELECT value FROM Table1
UNPIVOT (value FOR col IN (col1, col2, col3, col4)) AS unpvt