sql server(find the count of table base on condition) - sql

i have a table like following
RequestNo Facility status
1 BDC1 Active
1 BDC2 Active
1 BDC3 Active
2 BDC1 Active
2 BDC2 Active
i want like this
RequestNo Facilty Count
1 BDC (1,2,3) 1
2 BDC(1,2) 1
the count should display based on Status with facilty.Fcilityv should take as BDC only

Try this, (assuming that your facility is fixed 4 character code)
SELECT RequestNo, Fname + '(' + FnoList + ')' Facilty, count(*) cnt
FROM
(
SELECT distinct RequestNo,
SUBSTRING(Facility,1,3) Fname,
stuff((
select ',' + SUBSTRING(Facility,4,4)
from Dummy
where RequestNo = A.RequestNo AND
SUBSTRING(Facility,1,3) = SUBSTRING(A.Facility,1,3)
for xml path('')
) ,
1, 1, '') as FnoList
FROM Dummy A
) x
group by RequestNo, Fname, FnoList;
SQL DEMO

This doesn't put any constraints on the length of the Facility field. It strips out the chars from the beginning and the numeric numbers from the ending:
SELECT RequestNo, FacNameNumbers, COUNT(Status) as StatusCount
FROM
(
SELECT DISTINCT
t1.RequestNo,
t1.Status,
substring(facility, 1, patindex('%[^a-zA-Z ]%',facility) - 1) +
'(' +
STUFF((
SELECT DISTINCT ', ' + t2.fac_number
FROM (
select distinct
requestno,
substring(facility, 2 + len(facility) - patindex('%[^0-9 ]%',reverse(facility)), 9999) as fac_number
from facility
) t2
WHERE t2.RequestNo = t1.RequestNo
FOR XML PATH (''))
,1,2,'') + ')' AS FacNameNumbers
FROM Facility t1
) final
GROUP BY RequestNo, FacNameNumbers
And the SQL Fiddle

Related

One column into multiple columns by type, plus concatenating multiples

I have a table like this:
Customer
Number
Type
1
234.567.8910
1
1
234.234.2345
2
2
234.567.5555
1
2
151.513.5464
1
3
845.846.8486
3
I am trying to include this information with information from another table (say... address), but in separate columns by type, and I want to concatenate values that are of the same type so that the return looks like this:
Customer
Cell
Home
Work
1
234.567.8910
234.234.2345
NULL
2
234.567.5555 & 151.513.5464
NULL
NULL
3
NULL
NULL
845.846.8486
When I use the STRING_AGG function, it appends the value for each line of that customer - even if it would be null when the function isn't applied, so the customer 1 has both the cell and home numbers repeated twice.
The only workaround I can find is to select each column in a subquery and join them together. Is that the only option?
Try with FOR XML and PATH.
Query
select [Customer],
stuff((
select distinct ',' + [Number]
from [your_table_name]
where [Customer] = a.[Customer]
and [Type] = 1
for xml path (''))
, 1, 1, '') as [Cell],
stuff((
select distinct ',' + [Number]
from [your_table_name]
where [Customer] = a.[Customer]
and [Type] = 2
for xml path (''))
, 1, 1, '') as [Home],
stuff((
select distinct ',' + [Number]
from [your_table_name]
where [Customer] = a.[Customer]
and [Type] = 3
for xml path (''))
, 1, 1, '') as [Work]
from [your_table_name] as a
group by [Customer];
You need conditional aggregation:
SELECT
Customer,
STRING_AGG(CASE WHEN Type = 1 THEN Number END, ' & ') Cell,
STRING_AGG(CASE WHEN Type = 2 THEN Number END, ' & ') Home,
STRING_AGG(CASE WHEN Type = 3 THEN Number END, ' & ') Work
From table

How to join rows in some table in SQL Server?

i want join some table then concatenation columns
MyTable
-------------------------------
ID RowId LangId Caption
-------------------------------
1 1 1 ڕۆشتن
2 1 2 Go
3 1 3 اذهب
4 2 1 ئاو
5 2 2 water
6 2 3 ماء
I want join concatenation Caption column ex: for RowId 1 'ڕۆشتن - Go - اذهب'
Desired output
--------------------
RowId Caption
--------------------
1 ڕۆشتن - Go - اذهب
2 ئاو- water - ماء
I seen link but can't help me
You can use string_agg():
select rowid,
string_agg(caption, ' ') within group (order by langid) as caption
from t
group by rowid;
You can use for xml for older vresion :
select r.rowid,
stuff( (select ' - '+t.caption
from table t
where t.rowid = r.rowid
order by t.LangId
for xml path('')
), 1, 1, ''
) as Caption
from (select distinct rowid from table ) r;
You can use string_agg() for newer version SQL Server 17+ :
select t.rowid,
string_agg(t.caption, ' ') within group (order by t.langid) as caption
from table t
group by t.rowid;
You can try the following query using for xml as shown below.
SELECT DISTINCT t.RowId,
STUFF((SELECT distinct ' - ' + t1.[Caption]
FROM
(
SELECT t.[RowId],
t2.Caption
FROM [TestTable] AS t
INNER JOIN [TestTable] AS t2 ON t2.[RowId] = t.[RowId]
)
t1
WHERE t.RowId = t1.RowId
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'') CaptionList
FROM
(
SELECT t.[RowId],
por.Caption
FROM [TestTable] AS t
INNER JOIN [TestTable] AS por ON por.RowId = t.RowId
)t;
Here is the live db<>fiddle demo.
Try this:
SELECT DISTINCT RowID,
STUFF((SELECT DISTINCT Sub.caption +' - '
FROM #tab Sub
WHERE Sub.rowid = Main.rowid
FOR XML PATH('')), 1, 1, '') AS Caption
FROM #tab Main
I think this Query can perfectly fit for your case.

How can I retrieve first second and third word of a String in SQL?

I need a query which would extract the first second and third word of a string.
I have approximately 5 words in each row and I need only the first three words out of 5 in the same row (1 row). Example "ATV BDSG 232 continue with other words".
I need only the first three words together in one row (in the same row) like "ATV BDSG 232" as a first row. The table has about 1000 rows and at the end of it I should have 1000 rows again but each row should contain only the first three words of the string.
I found a query which works fine for extracting first two like "ATV BDSG" discussed in stack overflow. The query is
"SELECT SUBSTRING(field1, 0, CHARINDEX(' ', field1, CHARINDEX(' ', field1, 0)+1))
FROM Table"
Can we derive this for extracting first three words?
Thanks in advance
If you don't want to create a dedicated function, you can use successive CROSS APPLYs:
SELECT
T.s,
FirstSpace.i,
SecondSpace.j,
ThirdSpace.k,
CASE
When ThirdSpace.k > 0 THEN LEFT(T.s, Thirdspace.k - 1)
ELSE T.S
END AS Phrase
FROM t
CROSS APPLY (SELECT CHARINDEX(' ', T.s, 1)) AS FirstSpace(i)
CROSS APPLY (SELECT CHARINDEX(' ', T.S, FirstSpace.i + 1)) AS SecondSpace(j)
CROSS APPLY (SELECT CHARINDEX(' ', T.s, SecondSpace.j + 1)) AS ThirdSpace(k)
gives you the results you need:
| s | i | j | k | phrase |
|----------------------------------------|---|---|----|------------------|
| ATV BDSG 232 Continue with other words | 4 | 9 | 13 | ATV BDSG 232 |
Things are easy, SQL Server provide STRING_SPLIT() function make that too easy
DECLARE #Var VARCHAR(100) = 'ATV BDSG 232 Continue with other words';
SELECT Word
FROM
(
SELECT Value AS Word,
ROW_NUMBER()OVER(ORDER BY (SELECT NULL)) RN
FROM STRING_SPLIT(#Var, ' ')
) T
WHERE RN <= 3;
But since you are working on 2012 version, you need to define your own function.
You can also take the hard way, first you need to get the first word, then replace it with '' and get the second word, then do the same for the 3rd word as
DECLARE #Var VARCHAR(100) = 'ATV BDSG 232 Continue with other words';
WITH FW AS
(
SELECT LEFT(#Var, CHARINDEX(' ', #Var)) FirstWord
),
SW AS
(
SELECT LEFT(REPLACE(#Var, FirstWord, ''),
CHARINDEX(' ', REPLACE(#Var, FirstWord, ''))) SecondWord
FROM FW
)
SELECT FirstWord,
SecondWord,
LEFT(REPLACE(REPLACE(V, FirstWord, ''), SecondWord, ''),
CHARINDEX(' ', REPLACE(REPLACE(V, FirstWord, ''), SecondWord, ''))
) ThirdWord
FROM
(
SELECT *, #Var V
FROM FW CROSS APPLY SW
) T
Demo
UPDATE
If you want to select the three first words then simply
SELECT SUBSTRING(Str, 0, CHARINDEX(' ', Str, CHARINDEX(' ', Str, CHARINDEX(' ', Str, 0)+1)+1)) Words
FROM Strings
Demo
--make some test data
declare #test as nvarchar(100) = 'my test string for words';
select 1 id, cast('my test string for words' as nvarchar(max)) word into #test;
insert #test (id,word) values (2,'a b c d e f g hhh yyyyyy') ;
insert #test (id,word) values (3,' a required test string d e f g hhh yyyyyy') ;
insert #test (id,word) values (4,'a quick test') ;
insert #test (id,word) values (5,'a test') ;
insert #test (id,word) values (6,'last') ;
--break up letters, count the first 3 words
;WITH CTE AS (SELECT 1 x, substring(#test,1,1) charx
UNION ALL
SELECT X + 1, substring(#test,x + 1,1) from CTE WHERE x < len(#test)
)
select * from cte c3 where (SELECT count(0) cnt FROM CTE c1 JOIN CTE c2 on c1.x <= c3.x and c1.x + 1 = c2.x and c1.charx =' ' and c2.charx != ' ') < 3
;WITH tabx as (select id, cast(ltrim(word) as nvarchar(max)) 'word' from #test), --do some ltrim
CTE AS (
SELECT id, 1 x, substring(word,1,1) charx from tabx
UNION ALL
SELECT t.id, c.X + 1, substring(t.word,x + 1,1)
from tabx t
JOIN CTE c on c.id = t.id and x < len(t.word)
),
disj as
(select * from cte c3 where
(SELECT count(0) cnt
FROM CTE c1
JOIN CTE c2 on c1.id = c3.id and c1.id = c2.id and c1.x <= c3.x and c1.x + 1 = c2.x and c1.charx =' ' and c2.charx != ' '
) < 3
),
rj as
(select disj.id,disj.x, disj.charx z
from disj
where disj.x = 1
UNION ALL
select d.id, d.x, r.z + d.charx
FROM rj r
join disj d on r.id = d.id and r.x + 1 = d.x
)
select *
from rj r1
cross apply (select max(r2.x) TheRow from rj r2 where r1.id = r2.id) dq
where r1.x = dq.TheRow
order by r1.id;
--delete test data
drop table #test
/* This is not perfect - but interesting */
declare #t table (fullname varchar(100))
insert #t values('Mr Jones'),('Mrs Amy smith'),('Jim Smith'),('Dr Harry Web '),('Paul Fred andrew jones')
select fullname,
a.value as a ,
b.Value as b,
c.Value as c,
d.Value as d,
e.Value as e,
f.value as f
from #t
outer apply (select top 1 value from STRING_SPLIT(fullname, ' ')) a
outer apply (select top 1 value from STRING_SPLIT(fullname, ' ') where value not in (a.value )) b
outer apply (select top 1 value from STRING_SPLIT(fullname, ' ') where value not in (a.value,b.value ) ) c
outer apply (select top 1 value from STRING_SPLIT(fullname, ' ') where value not in (a.value,b.value,c.value )) d
outer apply (select top 1 value from STRING_SPLIT(fullname, ' ') where value not in (a.value,b.value,c.value,d.value) ) e
outer apply (select top 1 value from STRING_SPLIT(fullname, ' ') where value not in (a.value,b.value ,c.value,d.value,e.value) ) f
To Select First Word -
Select top 1 Ltrim(Rtrim(value)) FROM STRING_SPLIT(#input,' ')
To Select Only Second Word -
Select Ltrim(Rtrim(value)) from STRING_SPLIT(#input,' ') Order by (Select NULL) OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY

How to include string text in a STUFF function (XML Path)?

Code:
ISNULL(LTRIM(STUFF( (SELECT ', ' + PL.Pipe_Product
FROM Reporting.PipelineInformation PL
WHERE PL.Project_ID = TI.Project_ID
FOR XML PATH('')), 1, 1, '')),'___________') AS Pipe_Product
How can I do this?
Another approach can be to add a ROW_NUMBER into the sub-query to decide whether a comma or and should be inserted. This should be faster since you don't have separate queries against the same PipelineInformation table both of which are correlated sub-queries.
DECLARE #PL TABLE (Pipe_Product VARCHAR(50), Project_ID INT)
INSERT #PL VALUES ('gas', 1),('oil', 1),('orange juice', 1), ('milk', 2), ('honey', 2)
SELECT
TI.Project_ID,
SUBSTRING((
SELECT
CASE WHEN RowNum = 1 THEN ' and ' ELSE ', ' END + T.Pipe_Product AS [text()]
FROM (
SELECT
Pipe_Product,
Project_ID,
ROW_NUMBER() OVER (ORDER BY Pipe_Product DESC) AS RowNum
FROM #PL
WHERE Project_ID = TI.Project_ID
) T
ORDER BY RowNum DESC
FOR XML PATH('')
), 3, 4000) AS [Pipe_Product]
FROM (SELECT 1 UNION ALL SELECT 2) AS TI (Project_ID)
The sample data above outputs:
Project_ID Pipe_Product
----------- -----------------------------
1 gas, oil and orange juice
2 honey and milk
Try this edited result instead:
reverse(stuff(reverse(ISNULL(LTRIM(STUFF((
SELECT ', ' + PL.Pipe_Product
FROM Reporting.PipelineInformation PL
WHERE PL.Project_ID = TI.Project_ID
FOR XML PATH('')
), 1, 1, '')), '___________')), charindex(' ,', reverse(ISNULL(LTRIM(STUFF((
SELECT ', ' + PL.Pipe_Product
FROM Reporting.PipelineInformation PL
WHERE PL.Project_ID = TI.Project_ID
FOR XML PATH('')
), 1, 1, '')), '___________'))), 2, ' dna '))

SQL XML path conversion results error

Actually I m begineer to SQL XML path as so making me professional, Got a scenario...
I have a CTE Function That results as
Data Chars NumberOfOccurance
12 1 1 appears (1 ) times
12 2 2 appears (1 ) times
xx x x appears (2 ) times
and CTE function is :
;with cte as
(
select Data , SUBSTRING(Data,1,1) as Chars,1 as startpos from #t
union all
select Data, SUBSTRING(Data, startpos+1,1) as char,startpos+1 from cte where startpos+1<=LEN(data)
)
select Data,Chars,Cast(Chars as varchar(1)) + ' appears (' + cast(COUNT(*) as varchar(5))+ ' ) times' as 'NumberOfOccurance' from cte
group by data, chars
Actually I just want to make my answer into this :
data Number_of_occurances
12 1 appears (1) times 2 appears (1) times
xx x appears (2) times
I have tries this :
; With Ctea as
(
select Data , SUBSTRING(Data,1,1) as Chars,1 as startpos from #t
union all
select Data, SUBSTRING(Data, startpos+1,1) as char,startpos+1 from ctea where startpos+1<=LEN(data)
)
select Data,Chars,REPLACE((SELECT (Cast(Chars as varchar(1)) + ' appears (' + cast(COUNT(*) as varchar(5))+ ' ) times') AS [data()] FROM Ctea t2 WHERE t2.Data = t1.data FOR XML PATH('')), ' ', ' ;') As Number_of_occurances from ctea as t1
group by t1.data, t1.Chars
It says :
Column 'Ctea.Chars' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
when I use temp table and getting my exact answer , but cant do it CTE
Can anyone make my result ?
The problem has nothing to do with your CTE. It actually lies in the following subquery:
SELECT (Cast(Chars as varchar(1)) +
' appears (' +
cast(COUNT(*) as varchar(5)) +
' ) times') AS [data()]
FROM Ctea t2
WHERE t2.Data = t1.data
FOR XML PATH('')
Note how you are using the aggregate COUNT(*) here as well as the column Chars. You need to group by at least Chars here:
SELECT (Cast(Chars as varchar(1)) +
' appears (' +
cast(COUNT(*) as varchar(5)) +
' ) times') AS [data()]
FROM Ctea t2
WHERE t2.Data = t1.data
GROUP BY t2.Chars
FOR XML PATH('')
Furthermore, you do not want to select or group by t1.Chars in the outer query because it will result in one row per value of Chars:
data chars Number_of_occurances
12 1 1 appears (1) times 2 appears (1) times
12 2 1 appears (1) times 2 appears (1) times
xx x x appears (2) times
Finally you should most likely be using the STUFF function, and not REPLACE, as you are trying to create a space-delimited list ("1 appears (1) times 2 appears (1) times"), not replace all space characters with a space and a semicolon ("1 ;appears ;(1) ;times ;2 ;appears ;(1) ;times").
Therefore your final query should be:
; With Ctea as
(
select Data , SUBSTRING(Data,1,1) as Chars,1 as startpos from #t
union all
select Data, SUBSTRING(Data, startpos+1,1) as char,startpos+1 from ctea
where startpos+1<=LEN(data)
)
select Data,
STUFF((SELECT cast(' ' as varchar(max)) + (Cast(Chars as varchar(1)) + ' appears (' + cast(COUNT(*) as varchar(5))+ ' ) times') AS [data()]
FROM Ctea t2
WHERE t2.Data = t1.data
GROUP BY t2.Chars
FOR XML PATH('')), 1, 1, '') As Number_of_occurances
from ctea as t1
group by t1.data
You can use FOR XML PATH like this for concatenation
;with cte as
(
select Data , SUBSTRING(Data,1,1) as Chars,1 as startpos from #t
union all
select Data, SUBSTRING(Data, startpos+1,1) as char,startpos+1 from cte where startpos+1<=LEN(data)
), CTE2 AS
(
select Data,Chars,Cast(Chars as varchar(1)) + ' appears (' + cast(COUNT(*) as varchar(5))+ ' ) times' as 'NumberOfOccurance' from cte
group by data, chars
)
SELECT Data,(SELECT NumberOfOccurance + ' ' FROM CTE2 c2 WHERE c2.Data = C1.Data FOR XML PATH(''),type).value('.','VARCHAR(MAX)') as Number_of_occurances
FROM CTE2 C1
GROUP BY Data