How to get result properly from MySQL table - php

This is my product table
and I want to get all product where product_sizes in (Xl, M)
I am trying into this code
SELECT `id`,`name`,`sell_price`,`product_sizes`,`product_colors`
FROM `view_product_listing`
WHERE `product_sizes` in ('XL', 'M')
return to me productId 15 and 16 but I want to productId 4,14,15,16

You should seriously avoid storing the sizes as CSV (comma separated) data, see below for an alternative table design. As a workaround, we can use FIND_IN_SET:
SELECT id, name, sell_price, product_sizes, product_colors
FROM view_product_listing
WHERE FIND_IN_SET('M', product_sizes) > 0 OR FIND_IN_SET('XL', product_sizes) > 0;
But note that a much better database design would be to have a separate table for products, sizes, and colors: (colors omitted)
products
id | name | sell_price |
4 | Women Maxi White Dress | 550.00 |
14 | Women Maxi Blue Dress | 700.00 |
15 | Women Fit and Flare Multicolor Dress | 750.00 |
16 | Floral Print Straight Kurta | 699.00 |
sizes
product_id | product_size
4 | XL
4 | M
14 | XL
14 | XXL
14 | L
14 | M
15 | XL
16 | M
Now we can use a straightforward join to find all products, and their metadata, which have either the medium or XL size:
SELECT
p.id,
p.name,
p.sell_price,
s.product_size
FROM products p
INNER JOIN sizes s
ON p.id = s.product_id
WHERE
s.product_size IN ('M', 'XL');

use find_in_set()
SELECT `id`,`name`,`sell_price`,`product_sizes`,`product_colors`
FROM `view_product_listing`
WHERE FIND_IN_SET(product_sizes,'XL,M')

If you are only searching for one size you could use the built-in MySQL function FIND_IN_SET()
SELECT `id`,`name`,`sell_price`,`product_sizes`,`product_colors`
FROM `view_product_listing`
WHERE FIND_IN_SET('XL', product_sizes)
However it only supports a single string (unless you add additional OR's, but that quickly becomes tedious).
The best approach would be to restructure your database so that the product sizes of items are in a seperate table with a reference to the item table and a size table.
That way you don't have to update your queries whenever you want to introduce new sizes and it will improve performance for your query.
A word of warning,
Do not attempt a LIKE clause, because search for LIKE '%XL%' will also match XXL

You can use FIND_IN_SET like this,
Version 1:
SELECT `id`,`name`,`sell_price`,`product_sizes`,`product_colors`
FROM `view_product_listing`
WHERE FIND_IN_SET('XL',product_sizes) OR FIND_IN_SET('M',product_sizes)
EDIT 1
There is one more approach to achieve this,
Version 2:
SELECT `id`,`name`,`sell_price`,`product_sizes`,`product_colors`
FROM `view_product_listing`
WHERE CONCAT(',',product_sizes,',') REGEXP ",(XL|M),"
Source link for second version.
EDIT 2
You product_sizes is having spaces after commas, which is not the behaviour find_in_set is expecting. To trim all spaces from that column,
UPDATE `table` SET `product_sizes` = REPLACE(`product_sizes`, ' ', '')
And now run any version query you want to try, it will work.

Related

Time and Material Reporting - Process to combine multiple columns and rows of different data types

I am quite new to MySql (3 months of YouTube tutorials) and most of my experience comes from PHP/HTML(4-5 years building websites) and I am trying to build a report for some data that I've started collecting.
Any help you can provide is greatly appreciated!
I have a MySql database that collects multiple columns of materials/products that correspond to a quantity of material that was used on a construction site.
The data currently look like this:
Material1 | Quantity1 | Material2 | Quantity2 | Material3 | Quantity3|
Concrete Bags| 35 | Hydroseed | 1300 | Diesel Fuel | 40 |
Straw Wattles| 32 | Wooden Stakes| 200 | Diesel Fuel | 30 |
Hydroseed | 1000 | Wooden Stakes| 100 | Diesel Fuel | 20 |
What query or process can I use to add the quantities of same name materials, and combine the material names?
Hydroseed has two entries, 1300 + 1000 = 2300; Diesel Fuel has three entries, 40 + 30 + 20 = 90; etc. Material1, Material2, Material3 = MaterialName
I want to display it in the following manner:
MaterialName | Quantity |
Concrete Bags | 35 |
Hydroseed | 2300 |
Straw Wattles | 32 |
Wooden Stakes | 300 |
Diesel Fuel | 90 |
I am not exactly sure how to approach this.
My current query looks like this:
("SELECT Material1 AS MaterialName FROM table UNION SELECT Material2 AS MaterialName FROM table UNION SELECT Material3 AS MaterialName FROM database GROUP BY MaterialName");
I do not know what to do with the Quantities - I do not know how to combine the two queries and have the totals match up with the correct material name - perhaps something of this sort:
("SELECT *, SUM(Quantity1 + Quantity2 + Quantity3) AS Quantity FROM table GROUP BY MaterialName");
You may do an aggregation over a union of the three types of column data:
SELECT material AS MaterialName, SUM(quantity) AS Quantity
FROM
(
SELECT Material1 AS material, Quantity1 AS quantity FROM yourTable UNION ALL
SELECT Material2, Quantity2 FROM yourTable UNION ALL
SELECT Material3, Quantity3 FROM yourTable
) t
GROUP BY material;
The basic strategy here is to bring the three pairs of material data into just two columns. Then, aggregate by material and find the sum of quantity, for each material.
Demo
By the way, your current data model is not good, and in general you should not be storing the same logical thing across multiple columns. Consider just having two columns, one for the material and the other for the quantity.

LIMIT result on join MySQL

take the case you have 2 table, for example tbCostumers and tbOrders.
I would like to display a summary list with all costumers, related orders and display them with a paginator.
Doing a join I can extract costumers list and all orders for each costumer, the result is something like:
idCostumer | name | ... | idProduct | productName | price | ...
Where the first n columns are all equal if the costumer has more than 1 order. So I can have:
1 | will | ... | 12 | product1 | 123 | ...
2 | bill | ... | 23 | product2 | 321 | ...
2 | bill | ... | 24 | product3 | 231 | ...
And so on
I'm trying to use LIMIT to extract only n records and using them with a paginator.
First question: if a costumer has more than 1 order, with this query I'll see n records, equal in the first column (id, name, ... and other costumer info) but different at the end, where there are products info. Is this 'correct'? Is there another way to extract this informations?
Second question: if I do that and I use a LIMIT, I could "cut" the result table between 2 (or more) records that represent the same customer; so, for example in the small table above, if I limit with 2 the third row will be lost, even if it's part of the row above, because is just another order of the same costumer.
I would like to limit the number of different idCostumer, in order to take exactly n costumers, even if they appear more than 1 times in the result table. Something like n different idCostumer, no matter if they are repeated.
Is this possible?
I hope it's clear, it was not easy to explain what I would like to achieve :)
Thank you!
You might want to have something like this:
SELECT * FROM (
(SELECT * FROM tbCustomers LIMIT 3) AS c
INNER JOIN tbOrders AS o ON o.customer = c.idcustomer
);
You can substitute the first asterisk with named columns and only receive your desired columns in the order you prefer (ie: SELECT c.name, o.price FROM...) .
Hope this works for you!
EDIT: changing the value of the LIMIT clause changes the number of the picked customers, of course.
EDIT 2: As Alvaro Pointed out, you'll probably need an order clause in the tbCustomers query.

sum a column filtered on another column

I am trying to get the sum of a column that is filtered by another column (distinct values of that column). I have tried "group by" "distinct" "with rollup" and many other variations. I have a bunch of columns showing a customer link. Most services have a redundant side, though some don't have redundancy. I am trying to get a bandwidth total for entire "fnn" column but excluding the redundant side value if it exists. If there is redundancy, then it will be indicated by having the same fnn (and same bandwidth value).
I have been struggling with this for ages. Any help would be fantastic. Solution in mysql or php.
my table looks like:
table name = cust_child
id | cust_name |cp_bw|cp_fnn |other_columns
1 | A | 30 | 11 |xxx
2 | A | 30 | 11 |xxx
3 | B |100 | 22 |xxx
4 | B |100 | 22 |xxx
5 | C |50 | 33 |xxx
Result should look like:
Total bandwidth = 180
SELECT SUM(c.cp_bw) AS bandwith
FROM cust_child c
INNER JOIN (
SELECT c.id
FROM cust_child c
GROUP BY c.cp_fnn
) t1 ON (t1.id = c.id)
http://sqlfiddle.com/#!2/01219/1

How to implement SQL statement to create either/or functionality

SQL statement question. Let's say I have the following in my mini-table. Let's say the table is "images."
ID | file_name |fruit_type |
================================
1 | ex.jpg | apple |
2 | am.png | apple |
3 | pl.jpeg | orange |
4 | e.png | orange |
5 | yep.png | apple |
OK. So when I call this, I want it to pull two pictures, and each should be a random selection from within a fruit_type. So apples should only be picked with apples. Oranges should only be picked with oranges. There should be no combination of both types.
Here's where it breaks down. Let's say the code I run is this:
$query="SELECT * FROM images WHERE fruit_type='apple'
OR fruit_type='orange' ORDER BY RAND() LIMIT 0,2";
This would return two random selections, like I want. However, this captures BOTH apple and orange types from that column.
I have been trying to identify an SQL statement that would allow it to either choose apples or oranges, but never both. I've come up short, so I'm turning to you all.
You can create two queries, first for apples, second for oranges and select random apple and random orange, then join them using UNION ALL clause.
The following will work, but it won't be a great option from a performance point of view if your table gets huge (millions of records). The below query takes all combinations of images that have the same fruit_type excluding matching images (you don't want the same image twice, do you?).
SELECT images1.*, images2.*
FROM images images1, images images2
WHERE images1.id != images2.id
AND images1.fruit_type = images2.fruit_type
ORDER BY RAND()
LIMIT 0,2
Since you don't have windowing functions in MySQL, you have to improvise. This post is adapted from this blog post by Quassnoi:
SELECT
distinct
#fi AS file_name,
fruit_type
FROM (
SELECT m.*
FROM (
SELECT #_f = NULL
) vars,
images m
ORDER BY
fruit_type, rand()
) mo
WHERE (CASE WHEN #_f IS NULL OR #_f <> fruit_type THEN #fi := file_name ELSE file_name END IS NOT NULL)
AND (#_f := fruit_type) IS NOT NULL

SELECT DISTINCT name but return all data

So I have a table full of cars:
Say there are 4 dodge vipers, there would be 4 entries in the database, all are identical apart from the carID.
carID | carname | carmodel | colour | reg
--------------------------------------------------
1 | viper | dodge | red | 123
2 | viper | dodge | red | 124
3 | viper | dodge | red | 125
4 | viper | dodge | red | 126
5 | R8 | audi | blue | 127
6 | R8 | audi | blue | 128
When a User searches for cars, I want to display only one dodge viper. However I want to pull all the info from that row, and every other distinct car.
So the output I desire is:
carID | carname | carmodel | colour | reg
--------------------------------------------------
1 | viper | dodge | red | 123
5 | R8 | audi | blue | 127
If I do:
SELECT DISTINCT * FROM cars
it pulls back all entries.
SELECT DISTINCT carname FROM cars
Only pulls back one of each, but I only have the car name.
Is there such a:
SELECT * FROM cars ORDER BY DISTINCT carname
Or something similar?
If you want to return he carId, then you can use the following which will return the min(carid) for each identical carname, etc:
select min(carId) as carId, carname, carmodel, colour
from cars
group by carname, carmodel, colour
See SQL Fiddle with Demo
You will see that I placed the carId in an aggregate function this is to make sure that MySQL will always return the expected value for the carId column. When you do not GROUP BY or aggregate the items in the SELECT list, you might return unexpected results. (see MySQL Extensions to GROUP BY)
From the MySQL Docs:
MySQL extends the use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause. ... You can use this feature to get better performance by avoiding unnecessary column sorting and grouping. However, this is useful primarily when all values in each nonaggregated column not named in the GROUP BY are the same for each group. The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause. Sorting of the result set occurs after values have been chosen, and ORDER BY does not affect which values the server chooses.
I don't understand, do you mean grouping ?
SELECT MAX(CarId) as carId, carname, carmodel, colour FROM cars GROUP BY carname, carmodel, colour;
Try this:
SELECT DISTINCT carname, otherCol1, otherCol2... FROM cars
SQLFiddle Here
As You editted the question and want to display your car_id, above solution is no good.
Here you go:
SELECT MIN(carId) AS carId, carname, carmodel, colour
FROM cars
GROUP BY carname, carmodel, colour
MIN or MAX SUM Whatever you use is just a projection...perception you can say

Categories