Select all rows contains same value in a column - mysql

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

Related

Multi Ordering 4 SQL columns with a single query

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.

SQL Validate a column with the same column

I have the following situation. I have a table with all info of article. I will like to compare the same column with it self. because I have multiple type of article. Single product and Master product. the only way that I have to differences it, is by SKU. for example.
ID | SKU
1 | 11111
2 | 11112
3 | 11113
4 | 11113-5
5 | 11113-8
6 | 11114
7 | 11115
8 | 11115-1-W
9 | 11115-2
10 | 11116
I only want to list or / and count only the sku that are full unique. follow th example the sku that are unique and no have variant are (ID = 1, 2, 6 and 10) I will want to create a query where if 11113 are again on the column not cout it. so in total I will be 4 unique sku and not "6 (on total)". Please let me know. if this are possible.
Assuming the length of master SKUs are 5 characters, try this:
select a.*
from mytable a
left join mytable b on b.sku like concat(a.sku, '%')
where length(a.sku) = 5
and b.sku is null
This query joins master SKUs to child ones, but filters out successful joins - leaving only solitary master SKUs.
You can do this by grouping and counting the unique rows.
First, we will need to take your table and add a new column, MasterSKU. This will be the first five characters of the SKU column. Once we have the MasterSKU, we can then GROUP BY it. This will bundle together all of the rows having the same MasterSKU. Once we are grouping we get access to aggregate functions like COUNT(). We will use that function to count the number of rows for each MasterSKU. Then, we will filter out any rows that have a COUNT() over 1. That will leave you with only the unique rows remaining.
Take that unique list and LEFT JOIN it back into your original table to grab the IDs.
SELECT ID, A.MasterSKU
FROM (
SELECT
MasterSKU = SUBSTRING(SKU,1,5),
MasterSKUCount = COUNT(*)
FROM MyTable
GROUP BY SUBSTRING(SKU,1,5)
HAVING COUNT(*) = 1
) AS A
LEFT JOIN (
SELECT
ID,
MasterSKU = SUBSTRING(SKU,1,5)
FROM MyTable
) AS B
ON A.MasterSKU = B.MasterSKU
Now one thing I noticed from you example. The original SKU column really looks like three columns in one. We have multiple values being joined with hypens.
11115-1-W
There may be a reason for it, but most likely this violates first normal form and will make the database hard to query. It's part of the reason why such a complicated query is needed. If the SKU column really represents multiple things then we may want to consider breaking it out into MasterSKU, Version, and Color or whatever each hyphen represents.

MySQL - Using FIND_IN_SET on Multiple Tables with No Relation

My database structure looks something as follows:
Database Name: Products
id ptype
1 Cups
2 Shirts
3 Cups
4 Mugs
5 Fabric
6 Mat
Database Name: Categories
id category ptype
1 Clothes Pants, Shirts, Tshirts
2 Other Mugs, Cups
I want to get a list of distinct ptype (products table) which aren't already listed in ptype of categories table
So the result would be
ptype
Fabric
Mat
I tried using the following mysql select query but it doesn't seem to work
SELECT p.ptype, c.ptype, FIND_IN_SET(p.ptype,c.ptype) FROM products as p, categories as c WHERE FIND_IN_SET(p.ptype,c.ptype) < 1
It returns value of FIND_IN_SET as 0 for all the ptypes of products table.
Remove the spaces after the commas in categories.ptype field to make the query work. find_in_set() compares the needle to a list of comma separated values and considers the space after the commas to be part of the strings it searches, therefore no match is found if you search for a string that does not contain the spaces.

SQL group by merge results

how to run query select "sub" grouped by "cat" to return something like this:
SQL query:
select sub
from post
where cat = 1
group by id
to return something like:
3,4,9,14,33,22
table "post"
id cat sub
1 1 3,4,9,14
2 2 1,2
3 2 4,5
4 1 33,22
5 3 1,4
thanks,
It is a very bad idea to store lists of things in character strings. For one thing, your ids are integers, but the strings are characters. More importantly, SQL has a great data structure for storing lists -- it is called a table. You should be using a junction table.
But, sometimes you are stuck with the data you have. In that case, you can use group_concat():
select group_concat(sub)
from post
where cat = 1;

Using mysql i need to retrieve which are inserted last in every category

My prob in brief:
I have two tables namely category and product.
table: category
id category
1 cat1
2 cat2
3 cat3
4 cat4
table: product
id productName category
1 test1 1
2 test2 2
3 test3 3
4 test4 4
5 test5 2
6 test6 2
My prob is:
I need products which are inserted last in every category.
How to solve this.
thanks in advance
You could add a create_time timestamp when a new a product has been added, and retrieve the newest by category be something like:
select max(create_time),category from product group by category
This is a variation of one of the most-common SQL questions asked here, the per-group maximum. See eg. this question for a variety of approaches.
The one I often use is a null-self-left-join, to select the row which has no value above it:
SELECT p0.*
FROM product AS p0
LEFT JOIN product AS p1 ON p1.category=p0.category AND p1.id>p0.id
WHERE p1.id IS NULL
This is assuming that id​s are allocated in order so the highest is the most recent. Normally it wouldn't be a good idea to rely on identity as an ordering mechanism; you'd typically add an added timestamp to each row to work on instead.
(Note this and many other per-group maximum functions can return more than one row when two rows have identical order columns. If this is a problem it could be avoided by using a UNIQUE ordering column; as a primary key id is already that. You can get a single row even when there are two maxima using SQL:2003's useful but rather ugly ROW_NUMBER() OVER windowing functions, but this is not supported by MySQL.)