I am very new to sql and I need a better approach for the below scenario.
Table
And I need to convert to below result.
You can try using unpivot. Here is the sample demo with data.
DECLARE #Table TABLE (
ID INT
,COL1 VARCHAR(3)
,COL2 VARCHAR(3)
,COL3 VARCHAR(3)
,COL4 VARCHAR(3)
)
INSERT INTO #TABLE VALUES
(1,'yes',null,'yes',null)
,(2,null,'yes',null,'yes')
,(3,null,null,'yes',null)
,(4,null,null,null,null)
,(5,null,'yes','yes',null)
,(6,null,null,null,null)
,(7,null,null,null,'yes')
SELECT id
,yes
FROM (
SELECT id
,col1
,col2
,col3
,col4
FROM #TABLE
where coalesce(col1, col2, col3, col4) is not null
) AS cp
UNPIVOT(yes FOR col IN (
col1
,col2
,col3
,col4
)) AS up
union
select id, null from #TABLE
where coalesce(col1, col2, col3, col4) is null
order by id
Do a UNION ALL, one select for each coln, and finally one SELECT for rows without any yes at all.
select id, 'col1' from tablename where col1 = 'yes'
union all
select id, 'col2' from tablename where col2 = 'yes'
union all
select id, 'col3' from tablename where col3 = 'yes'
union all
select id, 'col4' from tablename where col4 = 'yes'
union all
select id, cast(null as char(4)) from tablename
where 'yes' not in (coalesce(col1, 'x'),
coalesce(col2, 'x'),
coalesce(col3, 'x'),
coalesce(col4, 'x'))
If the only value is 'yes' (or NULL), then the last select can be done as
select id, cast(null as char(4)) from tablename
where coalesce(col1, col2, col3, col4) is null
Also, you can do it as the below.
;WITH CTE
AS
(
SELECT
B.ID,
B.yes,
B.col
FROM
(SELECT ID ,
ISNULL(COL1, '') COL1 ,
ISNULL(COL2, '') COL2,
ISNULL(COL3, '') COL3 ,
ISNULL(COL4, '') COL4
FROM #Tbl) A
UNPIVOT
(
col
FOR yes in (col1, col2, col3, col4)
) B
)
SELECT CTE.ID, CTE.yes FROM CTE
WHERE col = 'yes'
UNION ALL
SELECT DISTINCT ID, '' FROM CTE A
WHERE NOT EXISTS
(
SELECT 1 FROM CTE B WHERE
B.ID = A.ID AND
B.col = 'yes'
)
Result:
ID yes
----------- -----
1 COL1
1 COL3
2 COL2
2 COL4
3 COL3
4
5 COL2
5 COL3
6
7 COL4
Related
I need a unique record all the table and all the column how can I get
Table1
Col1 col2 col3 col4 col5
A. P. 20. 21
B. Q. 18. 19
C. R 17. 16
Table2
Col1 col2 col3 col4 col5
A. P. 51 58
B. Q. 60 65
C. R 18 25
D. S. 33. 31
Table3
Col1 col2 col3 col4 col5
A. P. 60. 13
B. Q. 75 61
E t 100 108
F. U. 91. 98
Output of table (I need like dz output)
Col1 col2 col3 col4 col5
A. P. 20. 21
B. Q. 18. 19
C. R 17. 16
D. S. 33. 31
E. T. 100 108
F. U. 91. 98
To get all the unique rows you can combine all the records from all three tables using union all and use distinct to get unique rows.
select distinct col2 ,col3 ,col4 ,col5 from (
select col2 ,col3 ,col4 ,col5 from Table1
union all
select col2 ,col3 ,col4 ,col5 from Table2
union all
select col2 ,col3 ,col4 ,col5 from Table3 )T
Or if you wan to show first record of each unique col1 and col2 value as in your output table use below query (I didn't use Col1 in the query since it's serial number for that table only):
with cte as (
select row_number() over (partition by Col2,Col3 order by sl)rn, col2 ,col3 ,col4 ,col5 from (
select 1 sl, col2 ,col3 ,col4 ,col5 from Table1
union all
select 2 sl, col2 ,col3 ,col4 ,col5 from Table2
union all
select 3 sl ,col2 ,col3 ,col4 ,col5 from Table3 )T
)select row_number() over (order by col1),Col1,Col2, Col3, Col4 from cte where rn=1
You seem to want the first result based on the first two columns. One method uses union all:
select col1, col2, col3, col4
from table1
union all
select col1, col2, col3, col4
from table2 t2
where not exists (select 1 from table1 t1 where t1.col1 = t2.col1 and t1.col2 = t2.col2)
union all
select col1, col2, col3, col4
from table3 t3
where not exists (select 1 from table1 t1 where t1.col1 = t3.col1 and t1.col2 = t3.col2) and
not exists (select 1 from table2 t2 where t2.col1 = t3.col1 and t2.col2 = t3.col2) ;
You can also do this with row_number():
select col1, col2, col3, col4k
from (select t.*,
row_number() over (partition by col1, col2 order by which) as seqnum
from ((select 1 as which, col1, col2, col3, col4
from table1
) union all
(select 2 as which, col1, col2, col3, col4
from table2
) union all
(select 3 as which, col1, col2, col3, col4
from table3
)
) t
) t
where seqnum = 1;
With an index on (col1, col2) in each of the tables, I would expect the first version to be faster.
RAW DATA:
Col1 Col2 Col3 Col4
Ajay G1 B1 10.201.131.27
Ajay G1 B2 10.201.131.27
Ajay G1 B1 10.201.131.28
Ajay G1 B2 10.201.131.28
Ajay G1 B1 10.201.131.29
Ajay G1 B2 10.201.131.29
EXPECTED OUTPUT using Oracle 10g
Col1 Col2 Col3 Col4
Ajay G1 B1,B2 10.201.131.27,10.201.131.28, 10.201.131.29
Would be very glad if someone is able to help.
Query I used:
select * from (select
Col1,
Col2,
substr(regexp_replace(','||(LISTAGG(( CASE WHEN T1.COl3 IS NULL OR TRIM( T1.COl3 ) ='' THEN NULL ELSE T1.COl3 END), ',') WITHIN GROUP (ORDER BY T1.COl1 )), '(,[^,]+)(\1)+', '\1'),2) as COl3 ,
substr(regexp_replace(','||(LISTAGG(( CASE WHEN T1.COl4 IS NULL OR TRIM( T1.COl4 ) ='' THEN NULL ELSE T1.COl4 END), ',') WITHIN GROUP (ORDER BY T1.COl1 )), '(,[^,]+)(\1)+', '\1'),2) as COl4 ,
from T1
Group by
Col1
)abc
The OUTOPUT I GOT is below,
Col1 Col2 Col3 Col4
Ajay G1 B1,B2 10.201.131.27
Ajay G1 B1,B2 10.201.131.28
Ajay G1 B1,B2 10.201.131.29
Thanks in Advance.
To eliminate duplicates from LISTAGG you may use in Oracle 10 the row_number function to define the order of the duplication.
In the next step you pass to the LISTAGG function only the first duplicated (row_number = 1), all higher duplicates are reset to NULL that is ignored by LISTAGG.
Here the query
with t2 as (
select
COL1, COL2,
COL3,
row_number() over (partition by col1, col2, col3 order by null) as rn3,
COL4,
row_number() over (partition by col1, col2, col4 order by null) as rn4
from t)
select
COL1, COL2,
listagg(case when rn3 = 1 then COL3 end,',') within group (order by COL3) COL3,
listagg(case when rn4 = 1 then COL4 end,',') within group (order by COL4) COL4
from t2
group by COL1, COL2
result
COL1, COL2, COL3, COL4
Ajay G1 B1,B2 10.201.131.27,10.201.131.28,10.201.131.29
Note that this approach is far superior that the afterwards elimination with REGEXP as for non-trivial data you to often encounters ORA-01489: result of string concatenation is too long before you can start the elimination.
Note also, that you can upgrade to Oracle 19 (which can be considered overdue from the point of Oracle 10) and you may use the feature LISTAGG (DISTINCT with the same affect without the need of elimination duplicates. This version also elegantl yhandles the owerflow problems.
with t (Col1, Col2, Col3, Col4) as (
select 'Ajay', 'G1', 'B1', '10.201.131.27' from dual union all
select 'Ajay', 'G1', 'B2', '10.201.131.27' from dual union all
select 'Ajay', 'G1', 'B1', '10.201.131.28' from dual union all
select 'Ajay', 'G1', 'B2', '10.201.131.28' from dual union all
select 'Ajay', 'G1', 'B1', '10.201.131.29' from dual union all
select 'Ajay', 'G1', 'B2', '10.201.131.29' from dual)
, t1 as (
select Col1, Col2
, listagg(Col3, ',') within group (order by Col3) x
, listagg(Col4, ',') within group (order by Col4) y
from t
group by Col1, Col2
)
select Col1, Col2
, rtrim(regexp_replace(x || ',', '([^,]+,)\1+', '\1'), ',') Col3_
, rtrim(regexp_replace(y || ',', '([^,]+,)\1+', '\1'), ',') Col4_
from t1
;
COL1 CO COL3_ COL4_
---- -- ------------------------------ ------------------------------------------------------------
Ajay G1 B1,B2 10.201.131.27,10.201.131.28,10.201.131.29
I was showing it in two steps, but it can be in one step as well.
select Col1, Col2
, rtrim(regexp_replace(listagg(Col3, ',') within group (order by Col3) || ',', '([^,]+,)\1+', '\1'), ',') Col3_
, rtrim(regexp_replace(listagg(Col4, ',') within group (order by Col4) || ',', '([^,]+,)\1+', '\1'), ',') Col4_
from t
group by Col1, Col2
;
I have some incoming rows in the below format.
| Col1 | Col2 | Col3 |
| 1 | A | 1 |
| 1 | A | 1,2 |
| 1 | A | 1,3 |
| 1 | A | 2,4 |
Desired outputsql is
| Col1 | Col2 | Col3 |
| 1 | A | 1,2,3,4 |
Basically, group all rows based on Col1 and Col2 and then concatenate and remove duplicates from Col3.
SELECT COL1, COL2, {?????}
FROM TABLEA
GROUP BY COL1, COL2;
I could not think much at this moment. Any pointers would be much appreciated. I am inclined to WX2 database, but any ANSI compliant snippet would be helpful.
For Postgres use this:
select col1, col2, string_agg(distinct col3, ',') as col3
from (
select col1, col2, x.col3
from tablea, unnest(string_to_array(col3, ',')) as x(col3)
) t
group by col1, col2;
This is largely ANSI compliant except for the string_to_array() and string_agg() function.
You could try with transpose or concatenation functions. The difficulty comes from the fact that col3 is varchar and a conversion is needed to get the distinct values.
With MySQL :
SELECT col1, col2, GROUP_CONCAT(DISTINCT col3) AS col3 FROM
(SELECT col1, col2, CONVERT(SUBSTR(col3, 1), UNSIGNED INTEGER) AS col3 FROM (
SELECT 1 AS col1, 'A' AS col2, '1' AS col3 UNION ALL
SELECT 1 AS col1, 'A' AS col2, '1,2' AS col3 UNION ALL
SELECT 1 AS col1, 'A' AS col2, '1,3' AS col3 UNION ALL
SELECT 1 AS col1, 'A' AS col2, '2,4' AS col3
) AS t
UNION ALL
SELECT col1, col2, CONVERT(SUBSTR(col3, 3), UNSIGNED INTEGER) AS col3 FROM (
SELECT 1 AS col1, 'A' AS col2, '1' AS col3 UNION ALL
SELECT 1 AS col1, 'A' AS col2, '1,2' AS col3 UNION ALL
SELECT 1 AS col1, 'A' AS col2, '1,3' AS col3 UNION ALL
SELECT 1 AS col1, 'A' AS col2, '2,4' AS col3
) AS t1
) AS t2
WHERE col3 <> 0
Result :
col1 | col2 | col3
1 | A | 1,2,3,4
For SQL Server: first concatenate all col3 values using STUFF method and INSERT INTO CTE table.Based on this CTE tables split all rows as individual into single column based on CTE table.Finally concate all DISTINCT strings with help of STUFF.
CREATE TABLE #table ( Col1 INT , Col2 VARCHAR(10) , Col3 VARCHAR(10))
INSERT INTO #table ( Col1 , Col2 , Col3 )
SELECT 1 , 'A' , '1' UNION ALL
SELECT 1 , 'A' , '1,2' UNION ALL
SELECT 1 , 'A' , '1,3' UNION ALL
SELECT 1 , 'A' , '2,4'
;WITH CTEValues ( Colval ) AS
(
SELECT STUFF ( ( SELECT ',' + Col3 FROM #table T2 WHERE T2.Col2 =
T1.col2 FOR XML PATH('') ),1,1,'')
FROM #table T1
GROUP BY Col2
)
SELECT * INTO #CTEValues
FROM CTEValues
;WITH CTEDistinct ( SplitValues , SplitRemain ) AS
(
SELECT SUBSTRING(Colval,0,CHARINDEX(',',Colval)),
SUBSTRING(Colval,CHARINDEX(',',Colval)+1,LEN(Colval))
FROM #CTEValues
UNION ALL
SELECT CASE WHEN CHARINDEX(',',SplitRemain) = 0 THEN SplitRemain ELSE
SUBSTRING(SplitRemain,0,CHARINDEX(',',SplitRemain)) END,
CASE WHEN CHARINDEX(',',SplitRemain) = 0 THEN '' ELSE
SUBSTRING(SplitRemain,CHARINDEX(',',SplitRemain)+1,LEN(SplitRemain))
END
FROM CTEDistinct
WHERE SplitRemain <> ''
)
SELECT STUFF ( ( SELECT DISTINCT ',' + SplitValues FROM CTEDistinct T2
FOR XML PATH('') ),1,1,'')
I have an Oracle table with multiple columns some populated with a variable, there a large number of possible variables the example below is not exhaustive.
ID Col1 Col2 Col3 Col4 Col5 Col6
-------------------------------------
1 X2 B2
2 C3 D1 R4
3 B2 X2
4 E4 T1 W2
5 X2 B2
6 R4 D1
7 D1 R4 C3
I need to identify the number of distinct combinations where row 1, row 3 and row 5 in the above example are considered the same combination and rows 2 and 7 are also considered the same. So the desired result would look like:
Col1 Col2 Col3 Col4 Col5 Col6 Count(*)
------------------------------------------------
B2 X2 3
C3 D1 R4 2
E4 T1 W2 1
D1 R4 1
But if I use this:
SELECT Col1, Col2, Col3, Col4, Col5, Col6, Count(*)
FROM MyTable
GROUP BY Col1, Col2, Col3, Col4, Col5, Col6
ORDER BY Count(*) DESC
Then row 3 in my data is considered unique. However, it has the same combination as rows 1 and row 5. Also row 2 and 7 are not considered the same and the result is:
Col1 Col2 Col3 Col4 Col5 Col6 Count(*)
------------------------------------------------
X2 B2 2
C3 D1 R4 1
B2 X2 1
E4 T1 W2 1
R4 D1 1
D1 R4 C3 1
It looks like I need to sort the col variables before comparing them. But is there an elegant solution to doing this for large record sets (3 million+ records) with up to 20 columns of data in Oracle?
There are two ways that come to my mind. First you can write a function accepting six or more strings and concatenating them in order. Then:
select colstring, count(*)
from
(
select id, concat_sorted(col1, col2, col3, col4, col5, col6) as colstring
from MyTable
)
group by colstring;
Another way would be to make each column a separate record and use listagg on them, provided you have Oracle 11g or higher available:
select colstring, count(*)
from
(
select id, listagg (colx, ',') within group (order by colx) as colstring
from
(
select id, col1 as colx from MyTable
union all
select id, col2 from MyTable
union all
select id, col3 from MyTable
union all
select id, col4 from MyTable
union all
select id, col5 from MyTable
union all
select id, col6 from MyTable
)
group by id
)
group by colstring
Try like this,
WITH t AS (
SELECT 1 ID, 'X2' col1, 'B2' col2, NULL col3, NULL col4, NULL col5, NULL col6 FROM dual
UNION
SELECT 2, 'C3', 'D1', 'R4', NULL, NULL, NULL FROM dual
UNION
SELECT 3, 'B2', 'X2', NULL, NULL, NULL, NULL FROM dual
UNION
SELECT 4, 'E4', 'T1', 'W2', NULL, NULL, NULL FROM dual
UNION
SELECT 5, 'X2', 'B2', NULL, NULL, NULL, NULL FROM dual
UNION
SELECT 6, 'R4', 'T1', NULL, NULL, NULL, NULL FROM dual
UNION
SELECT 7, 'D1', 'R4', 'C3', NULL, NULL, NULL FROM dual
)
SELECT col1, col2, col3, col4, col5, col6, tot_count
FROM (
SELECT col1, col2, col3, col4, col5, col6, cnt,
MAX(cnt) OVER (PARTITION BY val) AS tot_count,
row_number() OVER (PARTITION BY val ORDER BY cnt DESC) AS rn
FROM (
SELECT col1, col2, col3, col4, col5, col6, val, count(*) OVER (PARTITION BY val) cnt
FROM (
SELECT A.ID, col1, col2, col3, col4, col5, col6, val
FROM (SELECT ID, col1, col2, col3, col4, col5, col6
FROM t
) A,
(SELECT ID, listagg( val,',') WITHIN GROUP(ORDER BY val DESC) AS val
FROM (
SELECT ID, val
FROM t
unpivot ( val FOR origin IN (col1, col2, col3, col4, col5, col6))
)
GROUP BY ID
)b
WHERE A.ID = b.ID
)
ORDER BY val
)t1
)t2
WHERE tot_count = cnt
AND rn = 1
ORDER BY tot_count DESC;
Table:
CREATE TABLE Table1 (
col1 INT,
col2 nvarchar(10),
col3 INT,
col4 INT
);
INSERT INTO Table1
(col1, col2, col3, col4)
VALUES
(1, 'welcome', 3, 4);
My table have different data type , col2 is nvarchar h can i do this ...
result:
col value
---------------
col1 1
col2 welcome
col3 3
col4 4
You can use the UNPIVOT operation to get your results
SELECT col, value
FROM
(SELECT CAST(col1 AS VARCHAR) AS col1, CAST(col2 AS VARCHAR) AS col2,
CAST(col3 AS VARCHAR) AS col3, CAST(col4 AS VARCHAR) AS col4
FROM Table1) p
UNPIVOT
(value FOR col IN
(col1, col2, col3, col4)
) AS unpvt;
Use:
SELECT 'col1' AS col,
CAST(t1.col1 AS NVARCHAR(10)) AS value
FROM TABLE_1 t1
UNION ALL
SELECT 'col2' AS col,
t2.col2 AS value
FROM TABLE_1 t2
UNION ALL
SELECT 'col3' AS col,
CAST(t3.col3 AS NVARCHAR(10)) AS value
FROM TABLE_1 t3
UNION ALL
SELECT 'col4' AS col,
CAST(t4.col4 AS NVARCHAR(10)) AS value
FROM TABLE_1 t4
Part of the problem is that you need to make the second column the same data type:
CAST/CONVERT
with rows(n)
as
(
select 1
union all
select n + 1
from rows
where n + 1 <= 4
)
select case n
when 1 then 'col1'
when 2 then 'col2'
when 3 then 'col3'
when 4 then 'col4'
end as col,
case n
when 1 then col1
when 2 then col2
when 3 then col3
when 4 then col4
end as value
from
(
select cast (col1 as varchar) col1,
col2,
cast (col3 as varchar) col3,
cast (col4 as varchar) col4,
n
from table1, rows
) x