Multi Ordering 4 SQL columns with a single query - mysql

Environment: MySQL 5.6
SqlTable name = CategoryTable
Sql Columns
CATEGORY_ID (INT)
CATEGORY_NAME (VARCHAR)
LEVEL (INT)
MOTHER_CATEGORY (INT)
I've tried with
SELECT
CATEGORY_ID, CATEGORY_NAME , LEVEL , MOTHER_CATEGORY
FROM
CategoryTable
But I don't know how to use the ORDER BY in order to get that result.
So the first line here are the columns, and from the second lines, there start the table content:
CATEGORY_ID CATEGORY_NAME LEVEL MOTHER_CATEGORY
1 MainCategory 0 0
2 -SubCategory1 1 1
3 --SubCategory2 2 2
4 ---SubCategory3 3 3
5 2Nd_Main_Category 0 0
6 -SubCategory1 1 5
7 --SubCategory2 2 6
8 ---SubCategory3 3 7
is there a way to achieve something like this with a mysql query?

You aren't very clear in what you are trying to achieve. I'll take a guess that you want to order using a multi-level parent child structure. there are some very complicated ways of handling such a feat within mysql 5.6, a DB that's not really ideal for such a structure, but I have come up with something simple myself that I use in my own apps. you create a special ordering field that creates a path of zero filled ids for each record.
ordering_path_field
/
/0000000001/
/
/0000000001/0000000002
/0000000003
/0000000003/0000000005
/0000000003/0000000005/0000000006
etc
so each record contains a path of each parent up to the root, using zero filled ids. then you can just sort by this field to get them in proper order. the drawbacks being that you'll have to set a max number of levels allowed, so that the ordering fields doesn't overflow, and also, moving a record to a new parent if ever needed would be a big pain.

Related

Select all rows contains same value in a column

I want to select all package_id that contain product_id 2.
In this case, package_id 1,3,5 has product_id 2
Table: product_package
package_id package_name product_id
---------------------------------------------
1 Gold 1,2,3
2 Platinum 4,5,12
3 Diamond 2,11,5
4 Titanium 3,5
5 Basic 2
I tried:
SELECT
*
FROM
product_package
WHERE product_id IN(2)
It is outputting package_id 3 and 5 only. How do I output this properly?
product_id structure is varchar(256). Should I change the structure or add Foreign keys?
We always recommend not to stored delimited columns see Is storing a delimited list in a database column really that bad?
But you can use FIND_IN_SET but this is always slow
SELECT
*
FROM
product_package
WHERE FIND_IN_SET(2,product_id)
package_id
package_name
product_id
1
Gold
1,2,3
3
Diamond
2,11,5
5
Basic
2
fiddle
First, let me explain what is happening in your query.
You have WHERE product_id IN(2), but product_id is a misnomer and should rather be product_ids, because it is multiple IDs unfortunately stored in a string. IN is made to look up a value in a list. Your list, however, only consists of one element, so you can just as well use the equality operator: WHERE product_id = 2.
What you have is WHERE string = number, so the DBMS has to convert one of the values in order to compare the two. It converts the string to a number (so '2' matches 2 and '002' matches 2, too, as it should). But your strings are not numbers. The DBMS should raise an error on '1,2,3' for instance, because '1,2,3' is not a number. MySQL, however, has a design flaw here and still converts the string, regardless. It just takes as many characters from the left as they still represent a number. '1' does, but then the comma is not considered numerical (yes, MySQL cannot deal with a thousand separator when convertings strings to numbers implicitly). So converting '1,2,3' to a number results in 1. Equally, '2,11,5' results in 2, so rather surprisingly '2,11,5' = 2 in MySQL. This is why you are getting that row.
You ask "Should I change the structure", and the answer to this is yes. So far your table doesn't comply with the first normal form and should thus not exist in a relational database. You'll want two tables instead forming the 1:n relation:
Table: package
package_id
package_name
1
Gold
2
Platinum
3
Diamond
4
Titanium
5
Basic
Table: product_package
package_id
product_id
1
1
1
2
1
3
2
4
2
5
2
12
3
2
3
11
3
5
4
3
4
5
5
2
You ask "or add Foreign keys?", and the answer is and add foreign keys. So with the changed structure you want product_package(product_id) to reference product(product_id) and product_package(package_id) to reference package(package_id).
Disregarding that you should not be storing multiple values in a single field, you can use LIKE operator to achieve what you are looking for. I'm going with assumptions:
all values are delimited with commas
all values are integers
there are no whitespaces (or any other characters besides integers and commas)
select * from product_package
where product_id like '2,%'
or product_id like '%,2,%'
or product_id like '%,2'
or product_id like '2'
Alternatively, you can use REGEXP operator:
select * from product_package
where product_id regexp '^2$|^2,.+|.+,2,.+|.+,2'
References:
MySQL LIKE
MySQL REGEXP

MySql: adding columns dynamically, as many as rows in another table

Transport table
id name
1 T1
2 T2
Pallets table
id name
1 P1
2 P2
Transport Pallet Capacity table
id transport_id pallet_id capacity
1 1 1 10
2 1 2 null
3 2 1 20
4 2 2 24
How to generate table like this:
id transport_id pallet_id_1_capacity pallet_id_2_capacity
1 1 10 null
2 2 20 24
Problem: pallets and transports can be added, so, neither quantity is known in advance.
For example, manager adds another pallet type and 'pallet_id_3_capacity' column should be generated (and can show null if no capacity data is yet available).
Another manager can fill 'transport pallet capacity' table later when notified.
Is there a way to build sql in mysql that will care about the above: specifically - dynamic number of pallets?
The SQL select-list must be fixed at the time you write the query. You can't make SQL that auto-expands its columns based on the data it finds.
But your request is common, it's called a pivot-table or a crosstab table.
The only solution is to do this in multiple steps:
Query to discover the distinct pallet ids.
Use application code to build a dynamic SQL query with as many columns as distinct pallet id values found in the first query.
Run the resulting dynamic SQL query.
This is true for all SQL databases, not just MySQL.
See MySQL pivot row into dynamic number of columns for a highly-voted solution for producing a pivot-table query in MySQL.
I am not voting your question as a duplicate of that question, because your query also involves transport_id, which will make the query solution a bit different. But reading about other pivot-table solutions should get you started.

Storing csv in MySQL field – bad idea?

I have two tables, one user table and an items table. In the user table, there is the field "items". The "items" table only consists of a unique id and an item_name.
Now each user can have multiple items. I wanted to avoid creating a third table that would connect the items with the user but rather have a field in the user_table that stores the item ids connected to the user in a "csv" field.
So any given user would have a field "items" that could have a value like "32,3,98,56".
It maybe is worth mentioning that the maximum number of items per user is rather limited (<5).
The question: Is this approach generally a bad idea compared to having a third table that contains user->item pairs?
Wouldn't a third table create quite an overhead when you want to find all items of a user (I would have to iterate through all elements returned by MySQL individually).
You don't want to store the value in the comma separated form.
Consider the case when you decide to join this column with some other table.
Consider you have,
x items
1 1, 2, 3
1 1, 4
2 1
and you want to find distinct values for each x i.e.:
x items
1 1, 2, 3, 4
2 1
or may be want to check if it has 3 in it
or may be want to convert them into separate rows:
x items
1 1
1 2
1 3
1 1
1 4
2 1
It will be a HUGE PAIN.
Use atleast normalization 1st principle - have separate row for each value.
Now, say originally you had this as you table:
x item
1 1
1 2
1 3
1 1
1 4
2 1
You can easily convert it into csv values:
select x, group_concat(item order by item) items
from t
group by x
If you want to search if x = 1 has item 3. Easy.
select * from t where x = 1 and item = 3
which in earlier case would use horrible find_in_set:
select * from t where x = 1 and find_in_set(3, items);
If you think you can use like with CSV values to search, then first like %x% can't use indexes. Second, it will produce wrong results.
Say you want check if item ab is present and you do %ab% it will return rows with abc abcd abcde .... .
If you have many users and items, then I'd suggest create separate table users with an PK userid, another items with PK itemid and lastly a mapping table user_item having userid, itemid columns.
If you know you'll just need to store and retrieve these values and not do any operation on it such as join, search, distinct, conversion to separate rows etc. etc. - may be just may be, you can (I still wouldn't).
Storing complex data directly in a relational database is a nonstandard use of a relational database. Normally they are designed for normalized data.
There are extensions which vary according to the brand of software which may help. Or you can normalize your CSV file into properly designed table(s). It depends on lots of things. Talk to your enterprise data architect in this case.
Whether it's a bad idea depends on your business needs. I can't assess your business needs from way out here on the internet. Talk to your product manager in this case.

select records in given ids sorting order

i have table lets say - Students,
with 5 records and id(s) are 1 to 5, now i want to select the records - in a way that result should come like given sorting order of id column
id column should be resulted - 5,2,1,3,4
is there any other way to do this - then separate db calls for ids?
single db call ?
I guess if you really want a hard-coded order, you could do something like this:
order by case id
when 5 then 0
when 2 then 1
when 1 then 2
when 3 then 3
when 4 then 4
else 999
end
Or more simply (as #Strawberry points out in the comments):
order BY FIELD(id,4,3,1,2,5) desc

Display records and child records from the same table in mysql

I have got a table in mysql whose data is as follows
code itemcode qty subcode
1 "A" 1 0
2 "B" 2 0
3 "C" 3 1
4 "D" 4 1
I would like to display these records as given below.
code itemcode qty subcode
1 "A" 1 0
3 "C" 3 1
4 "D" 4 1
2 "B" 2 0
The records which belong to the same group will have the same subcode. I would like to display all the records sorted by their code and below each instance of the code I would like to display records which have the same value in the subcode field. Can anybody help me.
I think it's bad design to do this kind of sorting in the query itself. At the client side, you'll probably do some rearranging anyway to show the relation, so I would do two queries:
SELECT * FROM table WHERE subcode=0 -> gives all top elements
SELECT * FROM table WHERE subcode!=0 ORDER BY subcode -> gives all sub elements grouped per parent node.
Then in your client code you can easily rebuild the relationship in an object model and then display it any way you need.
If you really need it in a query, Oracle has some direct support for it:
SELECT * FROM table CONNECT BY PRIOR subcode=code. See http://docs.oracle.com/cd/B14117_01/server.101/b10759/queries003.htm#i2060615.
In MySQL, you'd have to resort to recursive stored procedures as described in e.g: http://evolt.org/node/4047/.