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.
Related
I'm trying to use the pivot function in SQL. The pivoted column has '000s of values but no more than 3 per row
The Original query shows;
ID
Name
ThemeID
11
Game A
44
11
Game A
791
11
Game A
1422
23
Game B
42
23
Game B
285
23
Game B
1256
What I need;
ID
Name
ThemeID1
ThemeID2
ThemeID3
11
Game A
44
791
1422
23
Game B
42
285
1256
Here is an option that uses a conditional aggregation. We also use the window function row_number() over() to calculate the row and column numbers
Note: I added Game B - 1500 to illustrate the "overflow"
Example
Declare #YourTable Table ([ID] int,[Name] varchar(50),[ThemeID] int) Insert Into #YourTable Values
(11,'Game A',44)
,(11,'Game A',791)
,(11,'Game A',1422)
,(23,'Game B',42)
,(23,'Game B',285)
,(23,'Game B',1256)
,(23,'Game B',1500) -- Added to illustrate more than 3
Select ID
,Name
,ThemeID1 = max(case when CN = 1 then ThemeID end)
,ThemeID2 = max(case when CN = 2 then ThemeID end)
,ThemeID3 = max(case when CN = 0 then ThemeID end)
From (
Select *
,RN = (row_number() over (partition by ID,Name order by ThemeID)-1) / 3
,CN = row_number() over (partition by ID,Name order by ThemeID) % 3
From #YourTable
) A
Group By ID,Name,RN
Results
ID Name ThemeID1 ThemeID2 ThemeID3
11 Game A 44 791 1422
23 Game B 42 285 1256
23 Game B 1500 NULL NULL
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]
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
I have the following table:
sno name pid amount total
1 Arif 0 100 null
2 Raj 1 200 null
3 Ramesh 2 100 null
4 Pooja 2 100 null
5 Swati 3 200 null
6 King 4 100 null
I want total of each person such that it gives total sum of amount of its descendants.
For ex.
for RAJ total will be : total= amount of(raj+ramesh+pooja+swati+king)
for SWATI :Total=amount of swati only.
You could try something like this:
WITH hierarchified AS (
SELECT
sno,
amount,
hierarchyID = CAST(sno AS varchar(500))
FROM yourTable
WHERE pid = 0
UNION ALL
SELECT
t.sno,
t.amount,
hierarchyID = CAST(h.hierarchyID + '/' + RTRIM(t.sno) AS varchar(500))
FROM yourTable t
INNER JOIN hierarchified h ON t.pid = h.sno
)
UPDATE yourTable
SET total = t.amount + ISNULL(
(
SELECT SUM(amount)
FROM hierarchified
WHERE hierarchyID LIKE h.hierarchyID + '/%'
),
0
)
FROM yourTable t
INNER JOIN hierarchified h ON t.sno = h.sno;
Note that this query (which you can try on SQL Fiddle) would probably not be very efficient on a large dataset. It might do as a one-off query, and then it would likely be better to organise updating the totals each time the table is updated, i.e. using triggers.
I have order table
OrderId OrderStatusId CurrencyId PromotionCode
------------------------------------------------------
137 5 1 123a-123d
138 5 1 123a-123d-234c
I want to split the PromotionCode column like this:
Result:
OrderId OrderStatusId CurrencyId PromotionCode
-----------------------------------------------------
137 5 1 123a
137 5 1 123d
138 5 1 123a
138 5 1 123d
138 5 1 234c
Please help me ...
is it possible to do... any way plz help me asas possible....
If the promotion codes are always 4 characters long, the simplest way is probably a union:
select id, substring(code,1,4)
from YourTable
where LEN(code) >= 4
union all
select id, substring(code,6,4)
from YourTable
where LEN(code) >= 9
union all
select id, substring(code,11,4)
from YourTable
where LEN(code) >= 14
<etc>
For a more flexible solution, have a look at one of the various Split functions. After creating this function in your database, you can invoke it like:
select t.id, s.items
from YourTable t
cross apply
dbo.Split(t.code,'-') s
Both queries will produce the result as requested in your answer.