Scalar Function Coalesce Rows - sql

I am trying to get a scalar function to work that combines all the rows of display values.
When I run my distinct query I get results I expect:
SELECT DISTINCT [tblMSC_Notes].[Display]
FROM [tblMSC_Notes]
INNER JOIN [tblUnits_Notes] ON [tblUnits_Notes].[NotesID] = [tblMSC_Notes].[NoteID]
WHERE (([tblMSC_Notes].[Display] IS NOT NULL) AND ([tblUnits_Notes].[UnitID] = 15))
ORDER BY [tblMSC_Notes].[Display]
Rows Displayed:
♦
ML
No Cloak
What I am trying to do now is output the Display as a single field "♦, ML, No Cloak"
Looking at the various examples I think I am close but I keep getting errors with the query:
BEGIN
-- Declare the return variable here
DECLARE #Result nvarchar(Max)
SET #Result = ''
WITH Display_CTE (Display)
AS
(
SELECT DISTINCT [tblMSC_Notes].[Display]
FROM [tblMSC_Notes]
INNER JOIN [tblUnits_Notes] ON [tblUnits_Notes].[NotesID] = [tblMSC_Notes].[NoteID]
WHERE (([tblMSC_Notes].[Display] IS NOT NULL) AND ([tblUnits_Notes].[UnitID] = #UnitID))
ORDER BY [tblMSC_Notes].[Display]
)
SET #Result = (SELECT COALESCE(#Result + ', ','') as DisplayResult
FROM (
SELECT [tblMSC_Notes].[Display]
FROM [tblMSC_Notes]
WHERE [Display] IN (SELECT Display FROM Display_CTE)
ORDER BY [tblMSC_Notes].[Display]
)
)
-- Return the result of the function
RETURN #Result
END
I was going to make this a scalar function in another query.
Here is some sample data from the tblMSC_Notes table:
ID UnitID NotesID
1 1513 154
2 1513 154
3 5032 152
4 5032 155
5 5033 152
6 5033 155
7 5033 43
8 5034 43
9 5034 152
10 5034 155
11 5035 152
12 5035 155
13 5035 43
Here is join using the Note ID
ID UnitID NotesID Display
1 1513 154 LB
2 1513 154 LB
3 5032 152 AL
4 5032 155 PL
5 5033 152 AL
6 5033 155 PL
7 5033 43 N
8 5034 43 N
9 5034 152 AL
10 5034 155 PL
Mohd - Thanks for the help so I would just need to change my create function to:
CREATE FUNCTION [dbo].[fnAnnex03_MasterShipChart_Notes] (
-- Add the parameters for the function here
#UnitID int
)
RETURNS nvarchar(Max)
AS
BEGIN
-- Declare the return variable here
DECLARE #Result nvarchar(Max)
SET #Result = ''
SET #Result = (SELECT string_agg(DISTINCT [tblMSC_Notes].[Display], ',') FROM [tblMSC_Notes] INNER JOIN [tblUnits_Notes] ON [tblUnits_Notes].[NotesID] = [tblMSC_Notes].[NoteID] WHERE (([tblMSC_Notes].[Display] IS NOT NULL) AND ([tblUnits_Notes].[UnitID] = #UnitID)) ORDER BY [tblMSC_Notes].[Display])
-- Return the result of the function
RETURN #Result
END
GO

Could you please provide a FIDDLE for this example with data.
You just need to use string_agg in this query as below:
SELECT string_agg(DISTINCT [tblMSC_Notes].[Display], ',')
FROM [tblMSC_Notes]
INNER JOIN [tblUnits_Notes] ON [tblUnits_Notes].[NotesID] = [tblMSC_Notes].[NoteID]
WHERE (([tblMSC_Notes].[Display] IS NOT NULL) AND ([tblUnits_Notes].[UnitID] = #UnitID))
ORDER BY [tblMSC_Notes].[Display]

Related

Select query with same value in row

I have a table structure like this:
order_item_id order_id product_id
1 513 120
2 213 121
3 513 120
4 312 131
5 312 131
6 102 123
I want to have a SQL query where I can get the following results:
order_item_id order_id product_id
1 513 120
3 513 120
4 312 131
5 312 131
I used the following SQL query to fetch the results, but it doesn't help:
SELECT *
FROM `stg_83087_wc_order_product_lookup`
WHERE `order_id` = `order_id` and `product_id` = `product_id`
The only question I have to is to get the next value of the row so I can make the comparison here.
You basically need to check the existance of similar rows:
select *
from t
where exists (
select * from t t2
where t2.order_id = t.order_id
and t2.product_id = t.product_id
and t2.order_item_id != t.order_item_id
);

SQL query to get multihierarchy items

in my SQL Table i have following data
ID Level Description Code MasterID
1 1 Cars AD0 NULL
2 1 Trucks JF1 NULL
3 1 Items YU2 NULL
4 2 New Cars AS3 1
5 2 Used Cars TG4 1
6 2 Car parts UJ5 1
7 2 New trucks OL6 2
8 2 Used trucks PL7 2
9 2 Truck parts KJL8 2
10 2 Factory stuff UY9 3
11 2 Custom stuff RT10 3
12 3 Toyota 6YH11 4
13 3 BMW 9OKH12 4
14 3 VW 13 5
15 3 Tiers Type I J14 6
16 3 Tiers Type II J15 6
17 3 Tiers Type III ADS16 9
18 3 Seats SA17 6
19 3 Doors UU18 6
20 3 Lights 9OL19 6
21 4 Left light GH20 20
22 4 Right light H21 20
23 4 Left door JHJ22 19
24 4 Michelin UY23 16
25 4 Vredestein GTF24 17
26 4 Dunlop 25 15
My achievement is to get all hierarchy data for each single item. For Exmaple, the outpu should look like as following
ID Level Description Code MasterId1 Description1 MasterId2 Description2 MasterId3 Description3
24 4 Michelin UY23 16 Tiers Type II 6 Car Parts 1 Cars
.
.
19 3 Doors UU18 6 Car Parts 1 Cars NULL NULL
.
.
10 2 Factory Stuff UY9 3 Items NULL NULL NULL NULL
.
.
3 1 Items NULL NULL NULL NULL NULL NULL NULL
.
.
If somebody can help or give an advise how to achieve this?
This is not dynamic but it could be pretty easily.
Using a recursive cte you can get the hierarchy for the entire table and self join a few times to get the table structure you want.
;WITH cte AS
(
SELECT *, ID AS [RootID], 1 AS [MasterLevel] FROM Table1
UNION ALL
SELECT t1.*, cte.[RootID], cte.[MasterLevel] + 1 FROM Table1 t1
JOIN cte ON t1.ID = cte.MasterID
)
SELECT r.ID, r.[Level], r.[Description], r.[Code],
m1.ID AS MasterId1, m1.[Description] AS Description1,
m2.ID AS MasterId2, m1.[Description] AS Description2,
m3.ID AS MasterId3, m1.[Description] AS Description3
FROM cte r
LEFT JOIN cte m1 ON m1.[RootID] = r.[RootID] AND m1.MasterLevel = 2
LEFT JOIN cte m2 ON m2.[RootID] = r.[RootID] AND m2.MasterLevel = 3
LEFT JOIN cte m3 ON m3.[RootID] = r.[RootID] AND m3.MasterLevel = 4
WHERE r.MasterLevel = 1
ORDER BY r.RootID DESC, r.MasterLevel
This would build a dynamic sql to get master and desciption fields based on the maximum Level value. or you could define how many levels you want to see by changing the #MaxLevel
DECLARE #Sql VARCHAR(MAX) = '',
#SelectSql VARCHAR(MAX) = '',
#JoinSql VARCHAR(MAX) = '',
#MaxLevel INT,
#idx INT = 1
SET #MaxLevel = (SELECT MAX([Level]) FROM Table1)
WHILE #idx < #MaxLevel
BEGIN
SET #SelectSql = #SelectSql + REPLACE(', m<index>.ID AS MasterId<index>, m<index>.[Description] AS Description<index> ', '<index>', #idx)
SET #JoinSql = #JoinSql + REPLACE(' LEFT JOIN cte m<index> ON m<index>.[RootID] = r.[RootID] AND m<index>.MasterLevel = <index> ', '<index>', #idx)
SET #idx = #idx + 1
END
SET #Sql = '
;WITH cte AS
(
SELECT *, ID AS [RootID], 0 AS [MasterLevel] FROM Table1
UNION ALL
SELECT t1.*, cte.[RootID], cte.[MasterLevel] + 1 FROM Table1 t1
JOIN cte ON t1.ID = cte.MasterID
)
SELECT r.ID, r.[Level], r.[Description], r.[Code]' + #SelectSql
+ 'FROM cte r ' + #JoinSql
+ 'WHERE r.MasterLevel = 0
ORDER BY r.RootID DESC, r.MasterLevel'
EXEC(#Sql)

Excluding groups from a resultset using criteria in that resultset

Using SQL Server 2008.
We are given a Code, say 020286 that gives us a starting resultset.
Starting data:
Code L R G
020286 2 703 1
030383 3 6 0
031847 4 5 0
021932 7 10 0
022499 8 9 0
020068 229 310 1
020866 231 306 1
020524 232 241 0
030772 233 234 0
031787 235 236 0
031859 237 238 0
031947 239 240 0
020964 242 323 1
021215 253 342 1
030728 343 344 0
020990 345 346 0
022521 347 354 0
Now I want to exclude rows whose L is between L and R of any rows whose G=1 (in the same resultset) excepting the given Code (essentially do "L between L and R" for all G=1 except the given Code), while still keeping all G=1.
Expected results:
Code L R G
020286 2 703 1
030383 3 6 0
031847 4 5 0
021932 7 10 0
022499 8 9 0
020068 229 310 1
020866 231 306 1
020964 242 323 1
021215 253 342 1
030728 343 344 0
020990 345 346 0
022521 347 354 0
Here is a table var with starting data.
declare #t table (Code nvarchar(10),L int, R int, G int)
insert into #t (Code, L, R, G)
select '020286',2,703,1 union
select '030383',3,6,0 union
select '031847',4,5,0 union
select '021932',7,10,0 union
select '022499',8,9,0 union
select '020068',229,610,1 union
select '020866',231,396,1 union
select '020524',232,241,0 union
select '030772',233,234,0 union
select '031787',235,236,0 union
select '031859',237,238,0 union
select '031947',239,240,0 union
select '020964',242,383,1 union
select '021215',253,342,1 union
select '030728',343,344,0 union
select '020990',345,346,0 union
select '022521',347,354,0
select * from #t
Presumably the given code is identifyable somehow, so I'll assume there's a variable available containing it, which I'm calling #given:
DECLARE #given nvarchar(10)
SELECT #given = '020286'
First identify the L of rows where G=1 and which are not the given code:
SELECT L INTO #L FROM #t WHERE G=1 AND Code <> #given;
Now we need to join each pair of consecutive values in #L into rows in a new table which can then be used to work out which rows in #t we dont want. There's a few ways of doing this, but this one is pretty easy and simple to follow:
SELECT L as [Low], Cast(0 as int) as [High]
INTO #Pairs
FROM #L
WHERE L < (SELECT Max(L) FROM #L);
UPDATE #Pairs SET [High] = (SELECT Min(L) FROM #L WHERE L > [Low]);
Now remove the unwanted rows in #t:
DELETE t
FROM #t t CROSS JOIN #Pairs p
WHERE L > p.[Low] AND L < p.[High]
What's left should be correct:
SELECT * FROM #t ORDER BY L
Using your stated criteria:
Exclude rows whose L is between L and R of any rows whose G=1 (in the same resultset) excepting the given Code (essentially do "L between L and R" for all G=1 except the given Code), while still keeping all G=1:
SELECT *
FROM #t
WHERE G = 1
OR NOT(L >= (SELECT MIN(L) FROM #t WHERE G = 1 AND NOT Code = '020286')
AND L <= (SELECT MAX(R) FROM #t WHERE G = 1 AND NOT Code = '020286'))
However, your expected results do not appear to match your stated criteria. You appear to be saying (for this data set and criteria) that you want to exclude G<>1 rows with L between 229 (MIN(L) where Code <> '020286') and 610 (MAX(R) where Code <> '020286'), but these rows don't appear to match the apparent intent of your stated criteria:
CODE L R G
030728 343 344 0
020990 345 346 0
022521 347 354 0
In fact, it would appear that your criteria is actually:
Exclude rows whose L is between L and R of any previous rows whose G=1 (in the same resultset) excepting the given Code (essentially do "L between L and R" for the previous G=1 except the given Code), while still keeping all G=1:
WITH Criteria
AS (SELECT Code, L, R, G
FROM #t
WHERE G = 1)
SELECT T.Code, T.L, T.R, T.G
FROM #t AS T
INNER JOIN Criteria
ON T.L >= Criteria.L
AND T.L <= Criteria.R
AND T.G <> 1
AND Criteria.Code <> '020286'
UNION ALL
SELECT Code, L, R, G
FROM Criteria
ORDER BY 1

grouping rows by csv column TSQL

I have a table with following sample data:
**CategoriesIDs RegistrantID**
47 1
276|275|278|274|277 4
276|275|278|274|277 16261
NULL 16262
NULL 16264
NULL 16265
NULL 16266
NULL 16267
NULL 16268
NULL 16269
NULL 16270
276|275|278 16276
276|275|278|274|277 16292
276|275|278|274|277 16293
276|275|278|274|277 16294
276|275|278|274|277 16295
276|275|278|274|277 16302
276|275|278|274|277 16303
276|275|278|274|277 16304
276|275|278|274|277 16305
276|275|278|274|277 16306
276|275|278|274|277 16307
I need to know :
1). which category has how many regisrtantids (like 277 has how many registrantids)
2). group the registrants by category so that i can find which registrants are in category 277 for example)
Do I need to create a function which generates a table from csv ? I have created a function but not sure if it will work in this situation with IN clause.
Please suggest
If you are looking for output below
Category Reg Count
277 12
274 12
47 1
276 13
278 13
275 13
SQL FIDDLE DEMO
Try this
SELECT Category,COUNT([RegistrantID]) AS [Reg Count] FROM
(
SELECT
Split.a.value('.', 'VARCHAR(100)') AS Category
,[RegistrantID]
FROM
(
SELECT
CONVERT(XML,'<C>'+REPLACE([CategoriesIDs],'|','</C><C>') + '</C>') AS Categories
, [RegistrantID]
FROM table1
) T CROSS APPLY Categories.nodes('/C') AS Split(a)
) T1
GROUP BY Category
You should normalise your data.
That said, try this.
;with c as (
select RegistrantID, CategoriesIDs, 0 as start, CHARINDEX('|', CategoriesIDs) as sep
from yourtable
union all
select RegistrantID,CategoriesIDs, sep, CHARINDEX('|', CategoriesIDs, sep+1) from c
where sep>0
)
select *, count(*) over (partition by CategoriesID)
from
(
select convert(int,SUBSTRING(CategoriesIDs,start+1,chars)) as [CategoriesID],
RegistrantID
from
(
select *,
Case sep when 0 then LEN(CategoriesIDs) else sep-start-1 end as chars
from c
) v
) c2
order by CategoriesID
If you have a "Categories" table, you can do this with the following query:
select c.CategoryId, count(*)
from t join
categories c
on ','+cast(c.CategoryId as varchar(255))+',' like '%,'+CategoriesId+',%'
group by c.CategoryId;
This will not be particularly efficient. But neither will breaking the string apart. You should really have an association table with one row per item (in your original table) and category.

select hierarchical item using function

I tried to generate Html from table. And to achieve that, I used function.
TABLE:
LayoutId LayoutDetailsId ParentId DetailSyntax DetailValue
--------------- --------------- --------------- --------------- ---------------
2 15 0 table htmlchild
2 16 15 tbody htmlchild
2 17 16 tr htmlchild
2 18 17 th No.
2 19 17 th Name
2 20 17 th Address
2 21 16 tr htmlchild
2 22 21 td 1
2 23 21 td Asha
2 24 21 td Flamboyan Street
2 25 16 tr htmlchild
2 26 25 td 2
2 27 25 td Jack
2 28 25 td Manggo Street
note that htmlchild in DetailValue is like "HasChild"
FUNCTION:
CREATE FUNCTION FnHtmlGenerator(#layoutId BIGINT, #parentId BIGINT = 0, #prevTopLevelHtml VARCHAR(MAX) = '')
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #currentHtml VARCHAR(MAX);
DECLARE #parentTable TABLE
(
layoutDetailsId BIGINT,
parentId BIGINT,
syntax VARCHAR(50),
val VARCHAR(50)
);
INSERT INTO #parentTable
SELECT
ld.LayoutDetailsId,
ld.ParentId,
ld.DetailSyntax,
ld.DetailValue
FROM Alert.LayoutDetails ld
WHERE ld.ParentId = #parentId
AND ld.LayoutId = #layoutId;
SELECT
#currentHtml = '<' + pt.syntax + '>{0}</' + pt.syntax + '>',
#parentId = pt.layoutDetailsId
FROM #parentTable pt;
SELECT
#prevTopLevelHtml =
CASE CHARINDEX('{0}', #prevTopLevelHtml)
WHEN 0 THEN #currentHtml
ELSE STUFF(#prevTopLevelHtml, CHARINDEX('{0}', #prevTopLevelHtml), 3, #currentHtml)
END;
SELECT
#prevTopLevelHtml =
CASE
(SELECT COUNT(0)
FROM Alert.LayoutDetails ld
WHERE ld.ParentId = #parentId)
WHEN 0 THEN #prevTopLevelHtml
ELSE FnHtmlGenerator(#layoutId, #parentId, #prevTopLevelHtml)
END;
RETURN #prevTopLevelHtml;
END
RESULT:
html
---------------------------------------------------
<table><tbody><tr><td>{0}</td></tr></tbody></table>
The problem is. My function only trace 1 path. the first tr and first td even th is not traced. How to make it trace all path ?
Because when you call
SELECT
#currentHtml = '<' + pt.syntax + '>{0}</' + pt.syntax + '>',
#parentId = pt.layoutDetailsId
FROM #parentTable pt;
... that returns the last row in the table, and assigns that to the variables, so the path is 2/15/16/25/28
You may want to look at the recursive abilities of common table expressions (eg: http://msdn.microsoft.com/en-us/library/ms186243(v=sql.105).aspx) and/or the Hierarchical data type if your database platform supports it (SQL Server 2008+,)