SQL Select stuff - sql-server-2005

I have project table.
This is my query which is fetching following results.
select top 5 proj_ID, Proj_NM
from project
Output:
proj_ID Proj_NM
-------------------
20 test1
21 test2
22 test3
24 test4
25 test5
I want to get this output instead. Can any one pls help.
proj_ID Proj_NM All_Proj_NM
---------------------------------
20 test1 test1,test2,test3,test4,test5
21 test2 test1,test2,test3,test4,test5
22 test3 test1,test2,test3,test4,test5
24 test4 test1,test2,test3,test4,test5
25 test5 test1,test2,test3,test4,test5

You can use FOR XML PATH for that
select top 5 proj_ID, Proj_NM,
(select STUFF( (select top 5 ',' + Proj_NM
from project
order by proj_id
FOR XML PATH('')
), 1, 1, '')) AS All_Proj_NM
from project
order by proj_ID

Try this
Select Distinct ST2.proj_ID,ST2.Proj_NM,
substring((Select ',' + ST1.Proj_NM AS [text()]
From project ST1
WHERE ST1.Proj_NM IN (SELECT Top 5 Proj_NM From Projects )
ORDER BY ST1.proj_ID
For XML PATH ('')),2, 1000) [Pr_Name]
From dbo.project ST2

Related

Grouping and Sorting By distinct values in Oracle's SQL

I have table like this:
id
full_name
1
John Smith
2
Smith John
3
Jim Jonson
4
JimJonson
I want to get something like this:
id
full_name
1
John Smith
3
Jim Jonson
So, I need SELECT DISTINCT full_name FROM table, so that
John Smith and Smith John to be one and the same, also
Jim Jonson and JimJonson
I hope I explained it well. Can you help me?
You can split the full_name values by initial capitals of name and surnames in unpivoted manner, and sort alphabetically, and combine by using LISTAGG() function, and apply MIN() aggregation at the last step such as
WITH t(id,full_name) AS
(
SELECT 1, 'John Smith' FROM dual UNION ALL
SELECT 2, 'Smith John' FROM dual UNION ALL
SELECT 3, 'Jim Jonson' FROM dual UNION ALL
SELECT 4, 'JimJonson' FROM dual
), t2 AS
(
SELECT id,
TRIM(SUBSTR(full_name,column_value,LEAD(column_value,1,LENGTH(full_name)) OVER (PARTITION BY id ORDER BY id,column_value)-1)) AS names
FROM t,
TABLE(CAST(MULTISET(SELECT REGEXP_INSTR(full_name,'[A-Z]+',1,level)
FROM dual
CONNECT BY level <= REGEXP_COUNT(full_name,'[A-Z]')) AS sys.odcivarchar2list ))
), t3 AS
(
SELECT id, LISTAGG(names,' ') WITHIN GROUP (ORDER BY id,names) AS full_name
FROM t2
GROUP BY id
)
SELECT MIN(id) AS min_id, full_name
FROM t3
GROUP BY full_name
ORDER BY min_id
Demo
Step-by-step. Read comments within code.
SQL> with test (id, full_name) as
2 -- sample data
3 (select 1, 'John Smith' from dual union all
4 select 2, 'Smith John' from dual union all
5 select 3, 'Jim Jonson' from dual union all
6 select 4, 'JimJonson' from dual
7 ),
8 temp as
9 -- split full name to rows
10 (select id,
11 regexp_substr(full_name, '[^ ]+', 1, column_value) val,
12 column_value cv
13 from test cross join
14 table(cast(multiset(select level from dual
15 connect by level <= regexp_count(full_name, ' ') + 1
16 ) as sys.odcinumberlist))
17 ),
18 temp2 as
19 -- aggregate full name with no space between "words"
20 (select id,
21 listagg(val, '') within group (order by val) full_name
22 from temp
23 group by id
24 ),
25 temp3 as
26 -- fetch only distinct values
27 (select min(b.id) id,
28 b.full_name
29 from temp2 b
30 group by b.full_name
31 )
32 -- finally, join TEMP3 and sample data
33 select b.id,
34 a.full_name
35 from test a join temp3 b on b.id = a.id
36 order by a.id;
ID FULL_NAME
---------- ----------------------------------------
1 John Smith
3 Jim Jonson
SQL>

PLSQL - Compare two comma separated and remove duplicate

I have comma separated values in two column and i wants to remove "common" values available in both column. Below is the sample data
col1 col2
1234, 5623, 1236,1567 5623, 9089,1567,2890,1234
145,126,1236,1478 1748,8956,1234,1478
Required Data
COL1 COL2
1236 9089,2890
145,126,1236 1748,8956,1234
Thanks
I've slightly modified your table and added the ID column which uniquely identifies a row, so it looks like this:
SQL> select * from test;
ID COL1 COL2
---------- ------------------------------ ------------------------------
1 1234, 5623, 1236,1567 5623, 9089,1567,2890,1234
2 145,126,1236,1478 1748,8956,1234,1478
This approach
splits all columns into rows (t_one is for col1, t_two is for col2)
inter CTE uses the INTERSECT set operator in order to find common values which should be removed from the result
t1_new and t2_new aggregate new column values (using the LISTAGG function)
the final SELECT returns the final result
Here it goes:
SQL> with
2 t_one as
3 (select id,
4 trim(regexp_substr(col1, '[^,]+', 1, column_value)) c1
5 from test,
6 table(cast(multiset(select level from dual
7 connect by level <= regexp_count(col1, ',') + 1
8 ) as sys.odcinumberlist))
9 ),
10 t_two as
11 (select id,
12 trim(regexp_substr(col2, '[^,]+', 1, column_value)) c2
13 from test,
14 table(cast(multiset(select level from dual
15 connect by level <= regexp_count(col2, ',') + 1
16 ) as sys.odcinumberlist))
17 ),
18 inter as
19 (select t1.id, t1.c1 cx from t_one t1
20 intersect
21 select t2.id, t2.c2 cx from t_two t2
22 ),
23 t1_new as
24 (select a.id, listagg(a.c1, ',') within group (order by a.c1) c1_new
25 from (select t1.id, t1.c1 from t_one t1
26 minus
27 select i.id, i.cx from inter i
28 ) a
29 group by a.id
30 ),
31 t2_new as
32 (select b.id, listagg(b.c2, ',') within group (order by b.c2) c2_new
33 from (select t2.id, t2.c2 from t_two t2
34 minus
35 select i.id, i.cx from inter i
36 ) b
37 group by b.id
38 )
39 select a.id, a.c1_new, b.c2_new
40 from t1_new a join t2_new b on a.id = b.id;
ID C1_NEW C2_NEW
---------- -------------------- --------------------
1 1236 2890,9089
2 1236,126,145 1234,1748,8956
SQL>

sql query to format the table

Here is my input table. I am trying to format the table as shown in the output below. Could you please help me with the sql query in mssql
id type code
100 A k20
100 A m30
100 B m30
100 B m30
101 B x10
101 B 20
102 A 101
Output Table
id A_CODE B_CODE
100 k20,m30 m30,m30
101 null x10,20
102 101 null
Many examples of PIVOT and String Aggregation, but here is an example of both
Example
Select *
From (
Select A.ID
,Item = [type]+'_CODE'
,Value = Stuff((Select Distinct ',' +[code] From YourTable Where [id]=A.[id] and [type]=A.[type] For XML Path ('')),1,1,'')
From (Select Distinct [id],[type] from YourTable) A
) Src
Pivot (max(Value) for Item in ([A_CODE],[B_CODE])) Pvt
Returns
ID A_CODE B_CODE
100 k20,m30 m30
101 NULL 20,x10
102 101 NULL
Try this:
SELECT D.id
,STUFF(
(SELECT ',' + A_Code
FROM (
SELECT id,code A_Code FROM #Tab WHERE type='A'
)E
WHERE E.id=D.id FOR XML PATH ('')) , 1, 1, '') A_Code
,STUFF(
(SELECT ',' + B_Code
FROM (
SELECT id,code B_Code FROM #Tab WHERE type='B'
)E
WHERE E.id=D.id FOR XML PATH ('')) , 1, 1, '') B_Code
FROM(
SELECT id
,CASE WHEN type='A' THEN code END A_Code
,CASE WHEN type='B' THEN code END B_Code
FROM #Tab
)D
GROUP BY D.id
Output:
id A_Code B_Code
100 k20,m30 m30,m30
101 NULL x10,20
102 101 NULL

select query to push null values to last column in address lines

I need to write a query to select the address lines so that the null values should be pushed to last columns. For example if the table has below data
ID, line1, line2, line3, line4, line5, line6
1 null test2 test3 test4 test5 test6
2 test1 test2 test3 test4 test5 test6
3 null null null null test5 test6
4 test1 test2 test3 null test5 test6
Output of the query should be as follow
ID, line1, line2, line3, line4, line5, line6
1 test2 test3 test4 test5 test6 null
2 test1 test2 test3 test4 test5 test6
3 test5 test6 null null null null
4 test1 test2 test3 test5 test6 null
I tried to concatenate the values with a delimiter by eliminating the nulls and then split the values again, but I am having difficulty in splitting the values back as the actual data contains characters which are not xml supported. Is there a easy way to do this with out creating functions to split? Below works when there are no characters in values which cause cast to xml fail
SELECT
ISNULL(Line1 + '|', '')
+ ISNULL(Line2 + '|', '')
+ ISNULL(Line3 + '|', '')
+ ISNULL(Line4 + '|', '')
+ ISNULL(Line5 + '|', '')
+ ISNULL(Line6 , '') as tmpaddr
FROM #addr1
SELECT DISTINCT id,
S.a.value('(/H/r)[1]', 'VARCHAR(100)') AS line1,
S.a.value('(/H/r)[2]', 'VARCHAR(100)') AS line2,
S.a.value('(/H/r)[3]', 'VARCHAR(100)') AS line3,
S.a.value('(/H/r)[4]', 'VARCHAR(100)') AS line4,
S.a.value('(/H/r)[5]', 'VARCHAR(100)') AS line5,
S.a.value('(/H/r)[6]', 'VARCHAR(100)') AS line6
FROM
(
SELECT *,CAST (N'<H><r>' + REPLACE(tmpaddr , '|', '</r><r>') + '</r></H>' AS XML) AS [vals]
FROM #addr ) d
CROSS APPLY d.[vals].nodes('/H/r') S(a)
Let me assume you are using SQL Server, based on the syntax conventions.
You can do what you want with cross apply and aggregation:
SELECT a.id, t.*
FROM #addr1 a CROSS APPLY
(SELECT MAX(CASE WHEN seqnum = 1 THEN line END) as line1,
MAX(CASE WHEN seqnum = 2 THEN line END) as line2,
MAX(CASE WHEN seqnum = 3 THEN line END) as line3,
MAX(CASE WHEN seqnum = 4 THEN line END) as line4,
MAX(CASE WHEN seqnum = 5 THEN line END) as line5,
MAX(CASE WHEN seqnum = 6 THEN line END) as line6
FROM (SELECT num, line, ROW_NUMBER() OVER (ORDER BY num) as seqnum
FROM (VALUES (1, a.line1),
(2, a.line2),
(3, a.line3),
(4, a.line4),
(5, a.line5),
(6, a.line6)
) v(num, line)
WHERE line IS NOT NULL
) t
) t;

How to get active records for each date in SQL

I have a table like below:
ID FID MDate Active
--------------------------
1 1 2009-05-25 1
1 2 2009-05-25 1
1 1 2010-02-04 0
1 3 2010-02-04 1
1 1 2009-04-01 0
1 1 2009-03-01 1
How to get active FId for each date?
I was trying like below:
SELECT DISTINCT
ID, MDate,
STUFF ((SELECT DISTINCT ',' + CAST(FID AS VARCHAR)
FROM
(SELECT ID, MDate, FID
FROM Table1
WHERE IsActive = 1) t
WHERE t.MDate = a.MDate
FOR XML PATH('')), 1, 1, '') colb
FROM
(SELECT ID, MDate, FID
FROM Table1
WHERE IsActive = 1) a
Somehow, it is giving partial results. In last row of the result of above query FID 3 is getting selected, there should be two FIDs 2, 3 as FID 2 is active since 2009-05-25 to till date.
I want output like below:
ID MDate FID
1 2009-03-01 1
1 2009-05-25 1,2
1 2010-02-04 2,3
How to get this in SQL?
You need to specify names of fields correctly, and no need for that number of sub-query:
;WITH Table1 AS (
SELECT * FROM (VALUES
(1, 1, '2009-05-25', 1),
(1, 2, '2009-05-25', 1),
(1, 1, '2010-02-04', 0),
(1, 3, '2010-02-04', 1),
(1, 1, '2009-04-01', 0),
(1, 1, '2009-03-01', 1))
AS t(ID, FID, ModifyDate, IsActive)
)
SELECT DISTINCT ID,
ModifyDate,
STUFF(( SELECT DISTINCT ',' + CAST(FID AS VARCHAR)
FROM Table1 t
WHERE t.ModifyDate = a.ModifyDate
FOR XML PATH('')),1,1,'') colb
FROM Table1 a
WHERE IsActive=1
Results:
ID ModifyDate colb
----------- ---------- ----------
1 2009-03-01 1
1 2009-05-25 1,2
1 2010-02-04 1,3
(3 row(s) affected)
I would use a CTE to filter out the active row first
; WITH
CTE AS
(
SELECT *
FROM Table1
WHERE IsActive = 1
)
SELECT ID, ModifyDate,
colb = STUFF ( (
SELECT ',' + CAST(FID AS VARCHAR(10))
FROM CTE x
WHERE x.ModifyDate = c.ModifyDate
FOR XML PATH ('')
) , 1, 1, '')
FROM CTE c
GROUP BY ID, ModifyDate
SELECT distinct [ID],[MDate],STUFF((SELECT ',' + CAST(FID AS VARCHAR) FROM [dbo].[tableName] WHERE [MDate] = a.[MDate] and active =1 FOR XML PATH('')),1 ,1 ,'') as FID
FROM [dbo].[tableName] AS a
WHERE active = 1
ORDER BY [MDate]
Sample Output as below:
ID MDate FID
1 2009-03-01 1
1 2009-05-25 1,2
1 2010-02-04 3
I think this is what you want.