Distinct rows based on One column in multi union - sql

I am working to build a hierarchical menu which is based on 4 table. after doing research the only solution i can think of is to do it with the UNION so far i am close but still i don't have the desired result.
4 Table which i am using are
Page Table with PageID as PK (Which holds actual pages for cms)
Article Table with PageID as FK (Which has article)
Article Category Table with PageID as FK (this table has article category)
Magazine Table with PageID as FK (Magazine table holds magazine information)
I had to make some changes to my database based on some suggestion made to my previous question which is directly related to this question, Previous question has details about database.
I need these columns PID, MENU, Handler,PageLangID, ParentID,IssueID, CatID,MenuPosition
to create my Hierarchical Menu I wrote the below which get me the required column but i am not able run the DISTINCT on Menu so that i will get only Unique Row and based on that result set i can create my menu.
When i try to add DISTINCT (Menu) it generates me syntax error.
I am not sure based on my requirement which is mention in previous question whether this approach is fine or i should take some other approach which is not ambiguous or more professional
QUERY
SELECT PID, MENU, Handler, PageLangID, ParentID,IssueID, CatID, MenuPosition
FROM (
--Pages Table
SELECT PageId AS PID,SUBSTRING(PageName,0,20) AS MENU,SUBSTRING(PageInternalLinkURL,0,24) AS Handler, PageLangID,PageInheritance AS ParentID, 1 AS IssueID, 1 AS CatID,
PageLinkPosition as MenuPosition FROM pg_Pages WHERE PageLangID = 1
UNION
--Article Table
SELECT p.PageID as PID, SUBSTRING(c.ArticleCategoryName,0,20) AS MENU,SUBSTRING(p.PageInternalLinkURL,0,24) AS Handler,LanguageID,p.PageID AS ParentID,IssueID,c.ArticleCategoryID AS CatID,
1 AS MenuPosition FROM art_Articles a JOIN art_Category c ON a.ArticleCategoryID = c.ArticleCategoryID JOIN pg_pages p ON p.PageID = a.PageID WHERE LanguageID =1
UNION
--Article Category Table
SELECT p.PageID AS PID, SUBSTRING(c.ArticleCategoryName,0,20) AS MENU,SUBSTRING(p.PageInternalLinkURL,0,24) AS Handler, LangID,p.PageID,1, ArticleCategoryID,
1 AS MenuPosition FROM art_Category c JOIN pg_Pages p ON c.PageID = p.PageID WHERE LangID =1
UNION
--Magazine Table
SELECT p.PageID AS PID, CAST(IssueCode AS varchar(10)),SUBSTRING(p.PageInternalLinkURL,0,24) AS Handler,LangID,p.PageID AS ParentID, m.IssueID AS IssueID, 1,
1 AS MenuPosition FROM Magazine m JOIN pg_pages p ON m.PageID = p.PageID WHERE LangID =1
) AS T WHERE T.PageLangID = 1 ORDER BY MenuPosition
OUTPUT
PID MENU Handler PageLangID ParentID IssueID CatID MenuPosition
----------- -------------------- ------------------------ ----------- ----------- ----------- ----------- ------------
5 Book Review Articles.aspx 1 5 5 18 1
5 Business Articles.aspx 1 5 5 16 1
5 Group News Articles.aspx 1 5 5 6 1
5 Infrastructure Articles.aspx 1 5 5 17 1
5 Politics Articles.aspx 1 5 1 1 1
5 Politics Articles.aspx 1 5 3 1 1
5 Politics Articles.aspx 1 5 4 1 1
5 Politics Articles.aspx 1 5 5 1 1
6 Book Review Article-Category.aspx 1 6 1 18 1
6 Business Article-Category.aspx 1 6 1 16 1
6 Chairman's Message Article-Category.aspx 1 6 1 9 1
6 Culture Article-Category.aspx 1 6 1 3 1
6 Economy Article-Category.aspx 1 6 1 2 1
6 Editorial Message Article-Category.aspx 1 6 1 8 1
6 Finance Article-Category.aspx 1 6 1 19 1
6 Group News Article-Category.aspx 1 6 1 6 1
6 Habtoor Leighton Gr Article-Category.aspx 1 6 1 5 1
6 Infrastructure Article-Category.aspx 1 6 1 17 1
6 Lifestyle Article-Category.aspx 1 6 1 20 1
6 People Article-Category.aspx 1 6 1 7 1
6 Politics Article-Category.aspx 1 6 1 1 1
6 Sports Article-Category.aspx 1 6 1 4 1
12 102 Default.aspx 1 12 3 1 1
12 103 Default.aspx 1 12 4 1 1
12 106 Default.aspx 1 12 1 1 1
12 109 Default.aspx 1 12 5 1 1
1 Home Default.aspx 1 0 1 1 10
11 Video Videos.aspx 1 10 1 1 10
2 About Us Page.aspx 1 0 1 1 20
5 Articles Articles.aspx 1 0 1 1 20
6 Categories Article-Category.aspx 1 0 1 1 25
3 News News.aspx 1 0 1 1 30
12 Archive Default.aspx 1 0 1 1 40
10 Multimedia Multimedia.aspx 1 0 1 1 60

You have used order by in the outer select statement.
As order by not working with the distinct statement,
So, Do the DISTINCT in a subselect and the ORDER BY in the outer select.
Hope this will be helpful to solve out your problem.

You need DISTINCT on all SELECT statements
SELECT DISTINCT PID, MENU, Handler, PageLangID, ParentID,IssueID, CatID, MenuPosition
...

Related

T-SQL How to configure Group by so that specific values would be correctly shown

My current T-SQL query provides the following results:
Query:
WITH CTE AS
(
SELECT SubscriberID, sum(valueMB) as ValuesMB
FROM dbo.InternetNetwork
GROUP BY SubscriberID
),
CTE2 AS (
SELECT ab.planID, a.SubscriberID, MAX(ValuesMB) as MaximumValue
FROM CTE AS a
left join
Subscriber as ab on a.SubscriberID= ab.SubscriberID
GROUP BY ab.planID, a.SubscriberID
)
select *
FROM CTE2 as b
ORDER BY b.MaximumValue desc
Output:
planID | SubscriberID | MaxValue
19 1555 97536.00
18 3528 97478.00
2 4029 93413.00
Query #2:
WITH CTE AS
(
SELECT SubscriberID, sum(valueMB) as ValuesMB
FROM dbo.InternetNetwork
GROUP BY SubscriberID
),
CTE2 AS(
SELECT ab.planID, MAX(ValuesMB) as MaximumValue
FROM CTE AS a
left join
Subscriber as ab on a.SubscriberID= ab.SubscriberID
GROUP BY ab.planID
)
SELECT pl.OperatorID, MAX(b.MaximumValue) as Super
FROM CTE2 as b
left join
Plan as pl on b.planID= pl.planID
GROUP BY pl.operatorID
ORDER BY pl.operatorID
Output #2:
OperatorID | Value
1 93413.00
2 86017.00
3 97536.00
I would like to also include a subscriberID, but I'm unable to figure out a way to do so, as the only way to do it, is including in the last SELECT and adding to GROUP BY, which when done, makes a mess of a result which is not accurate.
My desired output:
OperatorID | Value | SubscriberID
1 93413.00 4029
2 86017.00 164
3 97536.00 1544
internet network data:
SubscriberID ValuesMB
1 28
1 27
2 27
2 27
2 27
3 29
3 28
3 27
3 27
4 27
4 27
4 29
Subscriber Data:
SubscriberID PersonID PlanID
1 1 3
2 2 10
3 2 6
4 3 14
5 3 1
6 4 18
7 5 5
8 5 1
9 5 9
10 5 16
11 6 13
12 6 13
13 6 20
14 6 16
15 7 4
Plan data
PlanID OperatorID
1 1
2 1
3 2
4 2
5 2
6 2
7 2
8 2
9 2
10 2
11 2
12 3
13 3
14 3
15 3
16 3
17 3
18 3
19 3
20 3
The tables are somewhat like this related InternetNetwork-> Subscriber -> Plan. InternetNetwork contains how much each Subscribed has used. Each Subscriber has Plan associated with him. Each Plan contains a different Operator, there are only three. I wish to list all three operators, the data transferred by the subscriber of the plan that has the operator and Subscriber ID.
Window functions allow you to have fields in your select along with aggregate functions. You can do something like this
;WITH CTE AS
(
SELECT I.SubscriberID,
S.PlanID,
SUM(ValuesMB) OVER(PARTITION BY i.SubscriberID)as ValuesMB
FROM dbo.InternetNetwork I
JOIN Subscriber S
ON I.SubscriberID = S.SubscriberID
),
CTE2 AS
(
SELECT p.operatorID,
a.SubscriberID,
a.ValuesMB,
ROW_NUMBER() OVER(PARTITION BY p.operatorID ORDER BY a.ValuesMB DESC) as rn
FROM CTE a
join [Plan] P
on a.planID = P.planID
)
SELECT operatorID,
ValuesMB,
SubscriberID
FROM CTE2
where rn = 1

Row_Number based on two columns

I am looking to try to create the row number below, I have tried so many options but none seem to work. I would appreciate help.
I want the row order to look at sub_id and for each one do a sequential number, 1, 2, 3... I then want it to look at disc, and if that changes, go back to 1 again. I have tried concatenating them and using a key, but I can't seem to get it to work.
ID sub_id disc ROW_NUMBER
1 1 OT 1
1 1 OT 2
1 1 OT 3
1 2 DT 1
1 2 DT 2
1 3 SL 1
1 3 SL 2
1 4 PH 1
1 4 PH 2
1 4 OT 1
1 5 OT 1
1 5 PH 2
1 5 DT 1
1 6 DT 1
1 6 DT 2
1 6 DT 3
1 7 SL 1
1 7 SL 2
1 7 DT 1
1 8 SL 1
1 8 SL 2
1 8 SL 3
1 9 DT 1
1 9 DT 2
1 9 DT 3
1 10 PH 1
1 10 DT 1
1 10 DT 2
1 11 OT 1
1 11 OT 2
1 11 OT 3
1 12 OT 1
1 12 OT 2
1 12 OT 3
Update
row_number() OVER (PARTITION BY sub_id ORDER BY disc) does not work. The ordering goes wrong when sub_id changes to 2. at this point it needs to revert back to 1, but it is 2.
You seem to want:
row_number() over (partition by id, sub_id, disc order by id)
Note: You do not have a column that specifies the ordering of the columns -- or even uniquely identifies each row. Hence, this just uses order by id. You might want to include some other column there.
SELECT ID , sub_ID , disc , ROW_NUMBER() OVER (PARTITION BY sub_ID ORDER BY disc) from your table

SQL - Recursively finding count in another table

I have a table which has both category and subcategory such as this :
CategoryID CategoryName ParentCategoryID
1 CatA NULL
2 CatA1 1
3 CatA2 1
4 CatA3 1
5 CatB NULL
6 CatB1 5
7 CatC NULL
8 CatC1 7
I have another table where I have mappings
MappingID CategoryID ItemID
1 2 1
2 3 1
3 6 2
4 8 3
5 2 3
6 3 4
7 4 4
8 2 3
9 3 4
10 2 2
11 2 2
12 2 2
13 2 3
14 2 1
I need a result set where count of all items per category is displayed such as :
Category No. of Items
1 12
5 1
7 1
Could you someone guide me with this?
I have tried using a CTE :
WITH CTE (CategoryID, CategoryName, ParentID, CategoryParentName)
AS
(
SELECT CategoryID,
CategoryName,
ParentCategoryID,
CategoryName AS CategoryParentName
FROM [dbo].[Category]
WHERE ParentCategoryID IS NULL
AND IsDeleted = 0
UNION ALL
SELECT garages.CategoryID,
garages.CategoryName,
garages.ParentCategoryID,
traces.CategoryName AS CategoryParentName
FROM [dbo].[Category] AS garages
INNER JOIN CTE AS traces ON traces.CategoryID= garages.ParentCategoryID
)
SELECT gr.CategoryID, COUNT(map.CategoryID) AS Total
FROM CTE gr
INNER JOIN [dbo].[CategoryMapping] map ON gr.CategoryID = map.CategoryID
GROUP BY gr.CategoryID
If there are just two levels of categories than there is no need for recursive query
select coalesce(c.ParentCategoryID, c.CategoryID), count(*)
from Category c
join CategoryMapping m on c.CategoryID = m.CategoryID
group by coalesce(c.ParentCategoryID, c.CategoryID)
Obviously, categories without mapping won't be there.

SQL sum and link fields in multiple tables

Table 1
prodUID (*) plantID ItemName size qty
1 1 car med 5
2 1 car small 2
3 1 car large 8
4 1 truck small 7
5 1 truck med 0
6 1 truck large 4
7 1 van small 0
8 2 truck large 10
table2
UID(*) plantID table2_plan_tid table2_prodUID itemname size num wk
------------------------------------------------------------------------------
1 1 1 car med 3 41
2 1 2 car small 0 42
3 1 3 car large 6 41
4 1 4 truck small 1 44
5 1 5 truck med 10 45
6 1 6 truck large 1 43
7 1 7 van small 7 42
8 2 8 car med 10 41
table3
UID(*) plantid table3_wk table3_prodUID itemName size qty
------------------------------------------------------------------
1 1 41 2 car med 5
2 1 41 3 car large 7
3 1 43 7 van small 8
Result I'm trying to get is
for plantid = ? and wk between ? and ?
plantid = 1 and wk between 41 and 45
sum qty for table1 on plantID
sum wk for table2 on plantID and for weeks
sum qty on table with on plantID and for weeks
and some how link this with joins or subqueries to get
plantid Itemname qty num order
------------------------------------
1 car 15 9 12
1 truck 11 12 0
1 van 0 7 8
I can't seem to get the right outcome
I also considered putting in an itemNameID field in all tables
if that would make it easier
You can achieve what you want using this query:
SELECT table1.plantID, table1.ItemName, SUM(table1.qty), COALESCE(t2.num_sum, 0), COALESCE(t3.qty_sum, 0)
FROM table1
LEFT JOIN (SELECT plantid, itemname, SUM(num) AS num_sum
FROM table2
WHERE wk >= 41 AND wk <= 45
GROUP BY plantID, itemname) t2 ON table1.plantID = t2.plantID AND table1.ItemName = t2.ItemName
LEFT JOIN (SELECT plantid, itemname, SUM(qty) AS qty_sum
FROM table3
WHERE table3_wk >= 41 AND table3_wk <= 45
GROUP BY plantID, itemname) t3 ON table1.plantID = t3.plantID AND table1.ItemName = t3.ItemName
WHERE table1.plantid = 1
GROUP BY plantID, ItemName
See also live!
Basically, you join 3 queries in order to get what you want.
That would be a bit cleaner if you respect a certain harmony with you column names.

Parent child hierarchy in sql server

I have the following requirement,
Input
CID ParentID
1 10
2 1
3 1
4 2
5 2
6 2
7 2
8 4
9 4
10 NULL
20 25
30 38
15 51
17 71
when I pass child value as 4 following is my desired output:
Desired Output:
CID ParentID
2 4
1 2
10 1
NULL 10
4 2
4 8
2 4
2 5
2 6
1 3
2 7
    
Please help! Thanks in advance.
Your explanation and your data don't match. I think the row (4, 2) in the results should be (4,9). If I'm correct, then this should do what you want:
with Parents(lvl, CID, ParentID) as (
select 0, ParentID, CID
from T
where CID = 4
union all
select lvl+1, T.ParentID, T.CID
from T join Parents
on T.CID = Parents.CID
)
select CID, ParentID
from Parents
union all
select T.ParentID, T.CID
from T join Parents
on Parents.ParentID = T.ParentID;
SQL fiddle here.