Order alphabetically but according to a parent - mysql

Given the table below of categories, would there be a way where I return the names alphabetically, but include the ones with a parent_id under its parent?
ID
parent_id
name
1
NULL
Hardware
2
NULL
Software
3
NULL
Networking
4
1
Desktop
5
1
Printer
6
1
Laptop
7
2
Office
8
2
Windows
9
2
Other
10
3
Outage
11
3
Firewall
12
NULL
Accounts
13
12
New Account
14
12
Password Reset
This query is used with PHP, so I could always organized them through php code but it got me thinking if there was a way to do this in the query directly. I'm drawing a blank on how to approach this. This particular example only goes 1 level deep with children, so a solution that handles that I would be fine with at the moment. But is there an approach that could allow multiple levels of children as well?
For example, if I just pulled only parent categories (rows with a null parent_id) and ordered by name I'd have Accounts, Hardware, Software, Networking. But I'd like to have:
ID
parent_id
name
12
NULL
Accounts
13
12
New Account
14
12
Password Reset
1
NULL
Hardware
4
1
Desktop
6
1
Laptop
5
1
Printer
3
NULL
Networking
11
3
Firewall
10
3
Outage
2
NULL
Software
7
2
Office
9
2
Other
8
2
Windows

I finally came up with a solution, much simpler than I was expecting. This will work for my example, but will not take into account multiple levels deep.
SELECT p.id, p.parent_id, p.cat_name, COALESCE(c.cat_name, p.cat_name) as g
FROM category p
LEFT JOIN category c
ON p.parent_id = c.id
ORDER BY g, parent_id, cat_name
To account for children who's parent may be a child itself, I do not yet have a solution but feel like that answer lies in using WITH RECURSIVE.

Related

Limit selected results by unique selected IDs when using left joins

I have a table users and some other tables like images and products
Table users:
user_id user_name
1 andrew
2 lutz
3 sophie
4 michael
5 peter
6 oscor
7 anton
8 billy
9 henry
10 jon
Tables images:
user_id img_type img_url
1 0 url1
1 1 url4
2 0 url5
7 0 url7
8 0 url8
9 1 url9
Table Products
user_id prod_id
1 5
1 55
2 555
8 5555
9 5
9 55
I use this kind of SELECT:
SELECT * FROM
(SELECT user.user_id,user.user_name, img.img_type, prod.prod_id FROM
users
LEFT JOIN images img ON img.user_id = users.user_id
LEFT JOIN products prod ON prod.user_id = users.user_id
WHERE user.user_id <= 5) AS users
ORDER BY user.user_id ASC
The result should be the following output. Due to performance improvements, I use ORDER BY and an inner select. If I put a LIMIT 5 within the inner or outer select, things won't work. MySQL will hard LIMIT the results to 5. However I need the LIMIT of 5 (pagination) found unique user_id results which would lead to 9 in this case.
Can I use maybe an if-statement to push an array with found user_id and break/finish up the select when the array consist of 5 UIDs? Or can I modify somehow the select?
user_id user_name img_type prod_id
1 andrew 0 5
1 andrew 1 5
1 andrew 0 55
1 andrew 1 55
2 lutz 0 5
2 lutz 0 55
3 sophie null null
4 michael null null
5 peter null null
results: 9
LIMIT 5 and user_id <= 5 do not necessarily give you the same results. One reason: There are multiple rows (after the JOINs) for user_id = 1. This is because there can be multiple images and/or multiple products for a given 'user'.
So, first decide which you want.
LIMIT without ORDER BY gives you an arbitrary set of rows. (Yeah, it is somewhat predictable, but you should not depend on it.)
ORDER BY + LIMIT usually implies gathering all the potentially relevant rows, sorting them, then doing the "limit". There are sometimes ways around this sluggishness.
LEFT leads to the NULLs you got; did you want that?
What do you want pagination to do if you are displaying 5 items per page, but user 1 has 6 images? You need to think about this edge case before we can help you with a solution. Maybe you want all of user 1 on a page, even if it exceeds 5? Maybe you want to break in the middle of '1'; but then we need an unambiguous way to know where to continue from for the next page.
Probably any viable solution will not use nested SELECTs. As you are finding out, it leads to "errors". Think of it this way: First find all the rows you need to display on all the pages, then carve out 5 for the current page.
Here are some more musings on pagination: http://mysql.rjweb.org/doc.php/pagination

Duplicate or unpredictable results in MySQL

I'm trying to join a few tables in MySQL. Our setup is a little unique so I try to explain as good as I can.
I have a table 'INVENTORY' that represents the current items on stock.
These items are stored in a table 'COMPONENT'
Components are being used in installations.
Every user can have multiple installations and the same component can be used in multiple installation as well.
To uniquely map a component to an installation, it can be assigned to a PRODUCT. a product as has a 1-1 relationship with an installation. A component is not directly related to an installation
To finally assign a product to a specific installation a mapping table COMPOMENT_PRODUCT is used.
Example:
A component is like a part, lets say a screw. This screw is used in a computer. The very same screw can be used on multiple computers. But each computer can only be used on one specific installation.
TABLE COMPOMENT_PRODUCT
COMPOMENT_ID PRODUCT_ID
1 1
1 2
2 1
2 2
So we have the components C1 and C2 relevant for two installations.
TABLE INVENTORY
COMPOMENT_ID INSTALLATION_ID ON_STOCK
1 1 5
1 2 2
What I want to achieve
Now, I want to retrieve the inventory state for all components. But, not every component has an inventory record. In these cases, the ON_STOCK value from the inventory shall be NULL
That means, for this example I'd expect the following results
COMPOMENT_ID PRODUCT_ID ON_STOCK
1 1 5
1 2 2
2 1 NULL
2 2 NULL
But executing this query:
SELECT DISTINCT
COMPONENT_PRODUCT.COMPONENT_ID,
COMPONENT_PRODUCT.PRODUCT_ID,
INVENTORY.ON_STOCK
FROM INVENTORY
RIGHT JOIN COMPONENT_PRODUCT ON COMPONENT_PRODUCT.COMPONENT_ID =
INVENTORY.COMPONENT_ID
returns the following resultset:
COMPONENT_ID PRODUCT_ID ON_STOCK
1 1 5
1 2 5
1 1 2
1 2 2
2 1 (null)
2 2 (null)
Now, my next thought was, "of course, this is how joins behave, okay I need to group the results". But the way SQL works, the aggregation is not entirely predictable. SO when I
GROUP BY COMPONENT_PRODUCT.COMPONENT_ID,COMPONENT_PRODUCT.PRODUCT_ID
I get this result:
COMPONENT_ID PRODUCT_ID ON_STOCK
1 1 5
1 2 5
2 1 (null)
2 2 (null)
I have prepared a Fiddle here: http://sqlfiddle.com/#!9/71ca87
What am I forgetting here? Thanks in advance for any pointers.
Try this query -
SELECT DISTINCT
COMPONENT_PRODUCT.COMPONENT_ID,
COMPONENT_PRODUCT.PRODUCT_ID,
INVENTORY.ON_STOCK
FROM INVENTORY
RIGHT JOIN COMPONENT_PRODUCT ON COMPONENT_PRODUCT.COMPONENT_ID =
INVENTORY.COMPONENT_ID
AND COMPONENT_PRODUCT.PRODUCT_ID = INVENTORY.INSTALLATION_ID

Suggestion to design a devices stock mysql database tables for a planning events web app

What I have, a stock of devices that will consist of:
5 printers
5 phones
5 routers
5 UPS
And all that devices I rent them to be used on all kind of events (outside party, weddings and so on), this devices are used with another app to keep track of food orders to help waiters.
Now I plan to develop a web app that will help me to see if I have available devices to plan events and rent my devices. For example: someone is planning 1 event in 01.08.2017 and will need 3 printers 3 phones 3 routers and 1 UPS, another client will plan a event on 20.07.2017 that will need 4 printers 4 phones 4 routers and 1 UPS, and let's say someone will ask a event on 28.07.2017 that will needing 3 printers 3 phones 3 routers and 1 UPS, I will need to check on a date picker if the 28.07.2017 date that I will have available that device request ( in our case no, because there is a event on 01.08.2017), another note, let's put a 1 week time that the client will return the devices, so the 1 week will be the gap for returning time. And the same category devices will be considered same in a sense that for me it won't count if I have phone1 or phone4 available, for me will be still 1 phone free that I can use exemple phone1 == phone2 same for other devices(all are same model) .
What I'm thinking for the tables is something like this:
devices_type:
id name
1 printer
2 android phone
3 router
4 UPS
devices_stock:
id id_type name is_available
1 1 printer1 0
2 1 printer2 1
3 1 printer3 1
4 1 printer4 0
5 1 printer5 1
6 2 router1 1
.....
(and same for phones,wifi and ups)
events:
id device_type device_id rented_start rented_end
1 2 6 20.07.2017 27.07.2017
2 1 2 01.08.2017 08.08.2017
3 1 3 28.07.2017 04.08.2017
In the end I will want to select a date and check if I can rent the devices or not providing the number of them needed, but I will start on mysql tables design, but I feel that I miss something in my structure, any help and suggestion for a better table structure for my need?
I'm assuming something like this is what you're after, which would at least display the devices available.
SELECT DISTINCT S.device_id, S.name AS Device_Name
FROM devices_stock S, devices_rented R
WHERE 'date' < R.rented_start AND 'date' > R.rented_end AND S.device_id = R.device_id AND S.is_available != 0

Find all the leaf nodes below a subtree in a Tree structure in sql server

I've a tree structure, and its subsequent assignment table for customer categories in an sql server database.
CustomerCategory (CategoryID, ParentId)
CustomerInCategory(CustomerID, CategoryID)
If a CustomerCategory has any customer assigned to it, we can't add another subcategory to it. So, Customer can only be added to the lowest level in every sub tree. In other sense, the result of this query
SELECT * FROM `CustomerCategory` WHERE `CategoryId` NOT IN
(SELECT DISTINCT `parentid` FROM `CustomerCategory` WHERE `parentid` IS NOT NULL)
would yield leaf nodes. The Other thing is that, this tree might have subtrees of different levels, and we also, don't want to bound the number of levels in anyway, however, our users won't need more than 10 levels. Consider this as an illustration
CategoryID------ParentID---------------Name
1 NULL All Customers
2 1 Domestic
3 1 International
4 2 Independent Retailers
5 2 Chain Retailers
6 2 Whole Sellers
7 5 A-Mart
8 5 B-Mart
9 4 Grocery Stores
10 4 Restaurants
11 4 Cafes
CustomerID---------CustomerName----------Category
1 Int.Customer#1 3
2 Int.Customer#2 3
3 A-Mart.Branch#1 7
4 A-Mart.Branch#2 7
5 B-Mart.Branch#1 8
6 B-Mart.Branch#2 8
7 Grocery#1 9
8 Grocery#2 9
9 Grocery#3 9
10 Restaurant#1 10
11 Restaurant#2 10
12 Cafe#1 11
13 Wholeseller#1 6
14 Wholeseller#2 6
My requirement is something like this, "Given a node in Categories, Return All the Customers attached to any node below it".
How can I do it with sql?
Obviously this can be done with a recursive call in the code, but how can we do it in t-sql (without calling a stored procedure several times or using text-based search)?
Can any body, Use a CTE to solve this problem?
I have a result set of something like this in mind
CustomerID--------Customer Name----------------CategoryId----------CAtegoryName
12 Cafe#1 11 Cafes
12 Cafe#1 4 IndependentRetailers
12 Cafe#1 2 Demoestic
12 Cafe#1 1 AllCustomers
.
.
.
4 A-Mart.Branch#2 7 A-Mart
4 A-Mart.Branch#2 5 Chain Retailers
4 A-Mart.Branch#2 2 Domestic
4 A-Mart.Branch#2 1 All Customers
.
.
.
14 Wholeseller#2 6 WholeSellers
14 Wholeseller#2 2 Domestic
14 Wholeseller#2 1 All Customers
This is not necessarily a good Idea to layout a result like this, This would consume too much space, something that might not be required, yet, a search in such result set would be very fast. If I want to find all the customers below say categoryId = 2 , I would simply query
SELECT * FROM resultset where category ID = 2
Any suggestions to improve the data model is super welcomed! If It helps solving this problem.
Once again, I'm not fixated on this result set. Any other Suggestion that solves the problem,
"Given a node in Categories, Return All the Customers attached to any node below it", is well accepted.
You can use a CTE to recursively build a table containing all the parent-child relationships and use the where clause to get only the subtree you need (in my example, everyting under CategoryId 5) :
WITH CategorySubTree AS (
SELECT cc.CategoryId as SubTreeRoot,
cc.CategoryId
FROM CustomerCategory cc
UNION ALL
SELECT cst.SubTreeRoot, cc.CategoryId
FROM CustomerCategory cc
INNER JOIN CategorySubTree cst ON cst.CategoryId = cc.parentId
)
SELECT cst.CategoryId
FROM CategorySubTree cst
WHERE cst.SubTreeRoot = 5
You can modify this query to add whatever you need, for example, to get customers linked to the category nodes in the subtree :
WITH CategorySubTree AS (
SELECT cc.CategoryId as SubTreeRoot,
cc.CategoryId
FROM CustomerCategory cc
UNION ALL
SELECT cst.SubTreeRoot, cc.CategoryId
FROM CustomerCategory cc
INNER JOIN CategorySubTree cst ON cst.CategoryId = cc.parentId
)
SELECT cst.CategoryId,cic.CustomerId
FROM CategorySubTree cst
INNER JOIN CustomerInCategory cic ON cic.CategoryId = cst.CategoryId
WHERE cst.SubTreeRoot = 5
And of course you can join further tables to get labels and other needed information.

how can i get this answer + a query in mysql

I have a table category in my database in this form
catID catTitle catParent
1 electronics 0
2 laptop 1
3 mobile 1
4 hp 2
5 hp-dv6 4
6 nokia 3
how can i get catID 's that are parent of hp-dv6 ?
(1,2,4,5)
thanks
No it didn't worked
when choosing hp-dv6 I want to get this informations:
catID catTitle catParent
1 electronics 0
2 laptop 1
4 hp 2
5 hp-dv6 4
electronics -> laptop -> hp -> hp-dv6 -> model_no ....
number of sub cats is not specified
Use this :
SELECT * FROM category AS child
INNER JOIN category AS parent
ON child.catParent = parent.catID
WHERE parent.catTitle = 'hp-dv6'
There's no recursion built in to MySQL. Instead, choices include
joining the table to itself as often as could possibly be required
switching to another model (e.g. Nested Set)
handling the recursion at the application level (e.g. with a bit of PHP)