SQL: sorting child rows under parent row - sql

I have this table in my SQL Server database.
MenuID MenuText ParentMenu MenuOrder MenuLevel
-------------------------------------------------------------
1 Home 0 1 0
2 Administrator 0 2 0
3 Groups 2 1 1
4 Users 2 2 1
5 Permissions 2 3 1
6 Test Level2 3 1 2
7 Test Level3 6 1 3
I want to sort this table rows like this:
Home, Administrator are MenuLevel 0 items.
MenuLevel 1 items will be under its top level menus according to their ParentMenu column which points to the parent MenuID. The child items will be sorted according to their MenuOrder column.
I tried so many things, but couldn't get the idea how it will be done using query.

Try this one:
SQL Fiddle
;WITH Cte AS(
SELECT *,
DisplayOrder = CAST(ROW_NUMBER() OVER(PARTITION BY ParentMenu ORDER BY MenuOrder) AS VARCHAR(MAX))
FROM Test
WHERE
ParentMenu = 0
UNION ALL
SELECT
t.MenuID,
t.MenuText,
t.ParentMenu,
t.MenuOrder,
t.MenuLevel,
DisplayOrder = c.DisplayOrder + CAST(ROW_NUMBER() OVER(PARTITION BY t.ParentMenu ORDER BY t.MenuOrder) AS VARCHAR(MAX))
FROM Cte c
INNER JOIN Test t
ON c.MenuID = t.ParentMenu
)
SELECT
MenuID,
MenuText,
ParentMenu,
MenuOrder,
MenuLevel
FROM cte
ORDER BY DisplayOrder
Result:
| MenuID | MenuText | ParentMenu | MenuOrder | MenuLevel |
|--------|---------------|------------|-----------|-----------|
| 1 | Home | 0 | 1 | 0 |
| 2 | Administrator | 0 | 2 | 0 |
| 3 | Groups | 2 | 1 | 1 |
| 6 | Test Level2 | 3 | 1 | 2 |
| 7 | Test Level3 | 6 | 1 | 3 |
| 4 | Users | 2 | 2 | 1 |
| 5 | Permissions | 2 | 3 | 1 |

Related

How to add root second parent id to oracle table

I have a child-parent table like this (0 means that it is the top item):
itemID | parentItemId
---------------------
1 | 0
2 | 0
3 | 0
4 | 1
5 | 1
6 | 2
7 | 5
8 | 7
9 | 7
I want to add a new column that will be rootItemId according to this logic:
If it is a top item then rootItemId = itemID, else rootItemId will be item under the root (i.e. level - 2) itemID.
It will look like this:
itemID | parentItemId | rootItemId
----------------------------------
1 | 0 | 1
2 | 0 | 2
3 | 0 | 3
4 | 1 | 4
5 | 1 | 5
6 | 2 | 6
7 | 5 | 5
8 | 7 | 5
9 | 7 | 5
I can get this result using the following sql:
select itemID,
parentItemId,
itemID as rootItemId
from ItemTable
where itemID = 0
union
select itemID,
parentItemId,
connect_by_root parentItemId as rootItemId
from ItemTable
start with parentItemId in
(select itemID
from ItemTable
where parentItemId =0)
connect by prior itemID = parentItemId
Is there a more elegant and efficient way to achieve the same results?
Thanks

SQL counting likes-dislikes for recommendation system, collaborative filtering User-Based

The idea is that the user leaves likes-dislikes for different items, I need to get a list of users who have left the same ratings (likes and dislikes) as the selected user (USER_ID = 1), to determine their similarity.
RATING Column:
1 = like,
0 = dislike
Full table:
+---------+---------+--------+--------------------------------------------------+
| USER_ID | ITEM_ID | RATING | -EXAMPLE- |
+---------+---------+--------+--------------------------------------------------+
| 1 | 1 | 1 |-+
| 1 | 2 | 1 | |
| 1 | 3 | 1 | +-[1,1,1,0,0] user_1 vector of ratings
| 1 | 4 | 0 | | | | |
| 1 | 5 | 0 |-+ | | |
| 3 | 1 | 1 |----+ + + total_match with user_1 = 3 [1,0,0]
| 3 | 2 | 0 | | |
| 3 | 3 | 0 | | |
| 3 | 4 | 0 |----------+ |
| 3 | 5 | 0 |------------+
| 4 | 1 | 1 |
| 4 | 2 | 1 |
| 4 | 3 | 1 |
| 4 | 4 | 0 |
| 4 | 5 | 0 |
+---------+---------+--------+
Match computing:
user_3 likes_match with user_1 = 1
user_3 dislikes_match with user_1 = 2
total_match = likes_match + dislikes_match = 3
How to make a SQL query to get the following result:
+---------+-------------+----------------+-------------+
| user_id | likes_match | dislikes_match | total_match |
+---------+-------------+----------------+-------------+
| 3 | 1 | 2 | 3 |
| 4 | 3 | 2 | 5 |
+---------+-------------+----------------+-------------+
Any Ideas?
(This uses sqlite, but it shouldn't need much if anything to work on other databases):
Given the following table:
CREATE TABLE ratings(user_id INTEGER, item_id INTEGER, rating INTEGER
, PRIMARY KEY(user_id, item_id)) WITHOUT ROWID;
INSERT INTO ratings VALUES(1,1,1);
INSERT INTO ratings VALUES(1,2,1);
INSERT INTO ratings VALUES(1,3,1);
INSERT INTO ratings VALUES(1,4,0);
INSERT INTO ratings VALUES(1,5,0);
INSERT INTO ratings VALUES(3,1,1);
INSERT INTO ratings VALUES(3,2,0);
INSERT INTO ratings VALUES(3,3,0);
INSERT INTO ratings VALUES(3,4,0);
INSERT INTO ratings VALUES(3,5,0);
INSERT INTO ratings VALUES(4,1,1);
INSERT INTO ratings VALUES(4,2,1);
INSERT INTO ratings VALUES(4,3,1);
INSERT INTO ratings VALUES(4,4,0);
INSERT INTO ratings VALUES(4,5,0);
This query:
SELECT r1.user_id AS user_id
, sum(r1.rating) AS likes_match
, sum(CASE r1.rating WHEN 0 THEN 1 ELSE 0 END) AS dislikes_match
, count(*) AS total_match
FROM ratings AS r1
JOIN ratings AS r2 ON r2.user_id = 1
AND r1.item_id = r2.item_id
AND r1.rating = r2.rating
WHERE r1.user_id <> 1
GROUP BY r1.user_id
ORDER BY r1.user_id;
Prouduces:
user_id likes_match dislikes_match total_match
---------- ----------- -------------- -----------
3 1 2 3
4 3 2 5
You may need multiple subqueries to achieve desired result, please see the code below:
select res1.user_id,
sum(res1.likes_match1) as likes_match,
sum(res1.dislikes_match1) as dislikes_match,
sum(res1.likes_match1)+sum(res1.dislikes_match1) as total_match
from(
select res.user_id,
case
when res.rating=1 then count(res.rating)
else 0
end as likes_match1,
case
when res.rating=0 then count(res.rating)
else 0
end as dislikes_match1
from
(
select b.user_id as user_id,
case
when a.rating=1 and b.rating=1 then 1
else 0
end as rating
from have a
inner join have b
on a.item_id=b.item_id
and a.user_id=1
and b.user_id <>1
and a.rating=b.rating
) as res
group by res.user_id, res.rating) as res1
group by res1.user_id
;

Counting on multiple columns

I have a table like this:
+------------+---------------+-------------+
|store_number|entrance_number|camera_number|
+------------+---------------+-------------+
| 1 | 1 | 1 |
| 1 | 1 | 2 |
| 2 | 1 | 1 |
| 2 | 2 | 1 |
| 2 | 2 | 2 |
| 3 | 1 | 1 |
| 4 | 1 | 1 |
| 4 | 1 | 2 |
| 4 | 2 | 1 |
| 4 | 3 | 1 |
+------------+---------------+-------------+
In summary the stores are numbered 1 and up, the entrances are numbered 1 and up for each store, and the cameras are numbered 1 and up for each entrance.
What I want to do is count how many how many entrances in total, and how many cameras in total for each store. Producing this result from the above table:
+------------+---------------+-------------+
|store_number|entrances |cameras |
+------------+---------------+-------------+
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 1 | 1 |
| 4 | 3 | 4 |
+------------+---------------+-------------+
How can I count on multiple columns to produce this result?
You can do this with a GROUP BY and a COUNT() of each item:
Select Store_Number,
Count(Distinct Entrance_Number) as Entrances,
Count(Camera_Number) As Cameras
From YourTable
Group By Store_Number
From what I can tell from your expected output, you're looking for the number of cameras that appear, whilst also looking for the DISTINCT number of entrances.
This will work as well,
DECLARE #store TABLE
( store_number INT,entrance_number INT,camera_number INT)
INSERT INTO #store VALUES(1,1,1),(1,1,2),(2,1,1),(2,2,1),
(2,2,2),(3,1,1),(4,1,1),(4,1,2),(4,2,1),(4,3,1)
SELECT AA.s store_number, BB.e entrances,AA.c cameras FROM (
SELECT s,COUNT(DISTINCT c) c FROM ( SELECT store_number s,
CONVERT(VARCHAR,store_number) + CONVERT(VARCHAR,entrance_number) +
CONVERT(VARCHAR,camera_number) c FROM #store ) A GROUP BY s ) AA
LEFT JOIN
( SELECT s,COUNT(DISTINCT e) e FROM ( SELECT store_number s,
CONVERT(VARCHAR,store_number) + CONVERT(VARCHAR,entrance_number) e
FROM #store ) B GROUP BY s ) BB ON AA.s = BB.s
Hope it helped. :)

Can't figure out a simple SQL query

Might be very simple, but I've been digging fow a few days now... I just can't figure out how to make this SQL query in Access...
In reference to the tables below, i'm looking for the query that can extract all the ITEMS for a specific Shop (ie 1:Alpha) from a specific GROUP (ie 1:Tools), that are NOT in the report for 2014... in this case ITEMS.IDs 6, 8, 9 and 10!
Tables:
Years
ID | Year
-----------------------------------------------
1 | 2014
2 | 2015
Shops
ID | ShopName
-----------------------------------------------
1 | Alpha
2 | Bravo
Items
ID | StockNbr | Description | GroupID
-----------------------------------------------
1 | 00-1200 | Ratchet 1/4 | 1
2 | 00-1201 | Ratchet 1/2 | 1
3 | 00-1300 | Screwdriver Philips No1 | 1
4 | 01-5544 | Banana | 2
5 | 00-4457 | Apple | 2
6 | 21-8887 | Hammer | 1
7 | 21-6585 | Drill | 1
8 | 21-4499 | Multimeter | 1
9 | 21-5687 | Digital Caliper | 1
10 | 22-7319 | File Set | 1
...
Groups
ID | GroupName
-----------------------------------------------
1 | Tools
2 | Fruits
REPORTS
ID | YearID | ShopID | ItemID
-----------------------------------------------
1 | 1 | 1 | 1
2 | 1 | 1 | 2
3 | 1 | 1 | 3
4 | 1 | 1 | 4
5 | 1 | 1 | 7
6 | 1 | 2 | 5
7 | 1 | 2 | 8
8 | 1 | 2 | 10
I've tried this, but then I realize it doesn't take the shops into consideration, it'll list all items that are not listed in reports, so if reports has an item for shop 2, it won't list it either...
SELECT Items.ID, Items.StockNbr, Items.Description, Items.GroupID, Reports.YearID, Reports.ShopID
FROM Reports
RIGHT JOIN Items ON Reports.ItemID = Items.ID
WHERE (((Items.GroupID)=1) AND ((Reports.UnitID) Is Null))
ORDER BY Items.StockNbr;
Thank you!
I think you're looking for an anti-join. There are several ways to do this. Here's one using not in.
select i.* from items i
where i.GroupId = 1
and i.ID NOT IN (
select ItemID from reports r
where r.ShopID = 1
and r.YearID = 2014
)
If the table Reports does not reference Items.ID then there is no available relationship ShopID or YearID
select *
from items
left join reports on items.id = reports.itemid
where reports.itemid IS NULL

Unable to group data in a table

Id | MenuName | vc_Link | SubMenu1 | IsMainMenu | TypeOfMenu
-----------------------------------------------------------------------
1 | Home | NULL | NULL | 1 | 0
2 | Projects | NULL | NULL | 1 | 0
4 | Pr1 | NULL | 2 | 0 | 1
5 | Homesub | NULL | 1 | 0 | 1
6 | Pr1Sub1 | NULL | 4 | 0 | 2
The above is my table structure.
MenuName is names of menus and submenus
SubMenu1 is that submenus belong to a paticular menu whos id is saved as submenu1
MainMenu is a bit to know whether it is a menu or submenu.
typeofmenu is that
0 means main menu
1 means submenu
2 means submenu's submenu
Now my problem is i want to display it as below
1 Home
5 HomeSub
2 Projects
4 Pr1
6 Pr1Sub1
Id | MenuName | vc_Link | SubMenu1 | IsMainMenu | TypeOfMenu
-----------------------------------------------------------------------
1 | Home | NULL | NULL | 1 | 0
5 | HomeSub | NULL | 1 | 0 | 1
2 | Projects | NULL | NULL | 1 | 0
4 | Pr1 | NULL | 2 | 0 | 1
6 | Pr1Sub1 | NULL | 4 | 0 | 2
How to group like this?
Only after grouping like this i can make the result of this query as a tree structure showing menus and its submenus.
Something like this:
with menu_tree (id, menuname, level, sort_path) as (
select id,
cast(menuname as varchar(max)),
1 as level,
cast(id as varchar(max)) as sort_path
from menus
where submenu1 is null
union all
select m.id,
cast(replicate('_', p.level * 2) + m.menuname as varchar(max)) as menuname,
p.level + 1,
p.sort_path + '_' + cast(m.id as varchar(max))
from menus m
join menu_tree p on p.id = m.submenu1
)
select id, menuname
from menu_tree
order by sort_path;
SQLFiddle: http://sqlfiddle.com/#!3/6c4ba/1