I have 2 tables with the following structure:
Custom Pricing List
+------------+--------------+------------ +
| stock_code | stored_price | pricing_ref |
+------------+--------------+------------ +
Generic Pricing List
+------------+---------------------+--------------+-------------+--------------+
| stock_code | last_purchase_price | sales_price | description | qty_in_stock |
+------------+---------------------+--------------+-------------+--------------+
What I would like to return is the following:
+------------+---------------------+--------------+--------------+-------------+
| stock_code | last_purchase_price | sales_price | description | qty_in_stock |
+------------+---------------------+--------------+--------------+-------------+
Currently I am only querying the last table, using the following MySQL statement:
SELECT * FROM $table WHERE $column LIKE '%$term%' $or LIMIT 0,10
However I'd like to merge the 2 and still have the ability to do a wildcard search on the stock_code column.
All results MUST return the description and qty_in_stock. The sales_price in the result will be either stored_price from the Custom Pricing Table or sales_price from the Generic Pricing Table however stored_price takes precedence over sales_price from the Generic Pricing Table.
I understand I need to do a UNION query, however I am unsure how I could write it.
EDIT
Using the statement:
SELECT stock.*, price.*
FROM stock
LEFT INNER JOIN price
ON stock.stock_code = price.stock_code
WHERE stock.stock_code LIKE '%123%'
LIMIT 0,10
I now receive the following error:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INNER JOIN price
ON stock.stock_code = price.stock_code
WHERE stock.stoc' at line 3
I think you can join both the tables. Try this:
SELECT
generic.stock_code,
generic.last_purchase_price,
IFNULL(custom.stored_price, generic.sales_price) AS actual_price,
generic.description,
generic.qty_in_stock
FROM
Generic_Pricing_List generic
JOIN Custom_Pricing_List custom
on custom.stock_code = generic.stock_code
WHERE $column LIKE '%$term%' $or LIMIT 0,10;
If stored_price is not null and is defined, it will be picked up. Otherwise, sales_price will be picked up.
SELECT g.*
FROM Generic_Pricing_List g
LEFT JOIN Custom_Pricing_List c
ON g.stock_code = c.stock_code
WHERE g.Search_Column LIKE '%Term%'
LIMIT 0,10
Left Inner Join will get you all rows from Generic_Pricing_List and matching rows from Custom_Pricing_List.
SELECT Generic_Pricing_List.*, Custom_Pricing_List.stored_price, Custom_Pricing_List.pricing_ref
FROM Generic_Pricing_List
LEFT INNER JOIN Custom_Pricing_List
ON Generic_Pricing_List.stock_code = Custom_Pricing_List.stock_code
WHERE Generic_Pricing_List.Search_Column LIKE '%Term%'
LIMIT 0,10
SELECT t1.*, t2.field1, t2.field2
FROM $table1 AS t1
LEFT JOIN $table2 AS t2 ON (t1.stock_code = t2.stock_code)
WHERE t1.$column LIKE '%$term%' $or
LIMIT 0,10
simply use JOIN
You could use a union, but it's not required. The union, however, might be more efficient with larger data sets because "if"s can be expensive. Quick and dirty:
SELECT gpl.stock_code, last_purchase_price, if(stored_price is null, sales_price, stored_price) as sales_price, description, qty_in_stock
FROM Generic_Pricing_List gpl
LEFT JOIN Custom_Pricing_List cpl
ON gpl.stock_code = cpl.stock_code
AND cpl.stored_price != ''
AND cpl.stored_price IS NOT NULL
WHERE $column LIKE '%$term%' $or
LIMIT 0,10
Using a union:
SELECT * FROM
(SELECT gpl.stock_code, last_purchase_price, stored_price as sales_price, description, qty_in_stock
FROM Generic_Pricing_List gpl
JOIN Custom_Pricing_List cpl
ON gpl.stock_code = cpl.stock_code
AND cpl.stored_price != ''
AND cpl.stored_price IS NOT NULL
WHERE $column LIKE '%$term%' $or
UNION
SELECT gpl.stock_code, last_purchase_price, sales_price, description, qty_in_stock
FROM Generic_Pricing_List gpl
WHERE $column LIKE '%$term%' $or) foo
GROUP BY foo.stock_code
LIMIT 0,10
Be careful of the "or" statement being injected, as it might cause problems with the results.
Related
I read many threads about getting only the first row of a left join, but, for some reason, this does not work for me.
Here is my structure (simplified of course)
Feeds
id | title | content
----------------------
1 | Feed 1 | ...
Artists
artist_id | artist_name
-----------------------
1 | Artist 1
2 | Artist 2
feeds_artists
rel_id | artist_id | feed_id
----------------------------
1 | 1 | 1
2 | 2 | 1
...
Now i want to get the articles and join only the first Artist and I thought of something like this:
SELECT *
FROM feeds
LEFT JOIN feeds_artists ON wp_feeds.id = (
SELECT feeds_artists.feed_id FROM feeds_artists
WHERE feeds_artists.feed_id = feeds.id
LIMIT 1
)
WHERE feeds.id = '13815'
just to get only the first row of the feeds_artists, but already this does not work.
I can not use TOP because of my database and I can't group the results by feeds_artists.artist_id as i need to sort them by date (I got results by grouping them this way, but the results where not the newest)
Tried something with OUTER APPLY as well - no success as well.
To be honest i can not really imagine whats going on in those rows - probably the biggest reason why i cant get this to work.
SOLUTION:
SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
SELECT artist_id
FROM feeds_artists fa
WHERE fa.feed_id = f.id
LIMIT 1
)
WHERE f.id = '13815'
If you can assume that artist IDs increment over time, then the MIN(artist_id) will be the earliest.
So try something like this (untested...)
SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
SELECT
MIN(fa.artist_id) a_id
FROM feeds_artists fa
WHERE fa.feed_id = f.feed_id
) a
Version without subselect:
SELECT f.title,
f.content,
MIN(a.artist_name) artist_name
FROM feeds f
LEFT JOIN feeds_artists fa ON fa.feed_id = f.id
LEFT JOIN artists a ON fa.artist_id = a.artist_id
GROUP BY f.id
#Matt Dodges answer put me on the right track. Thanks again for all the answers, which helped a lot of guys in the mean time. Got it working like this:
SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
SELECT artist_id
FROM feeds_artists fa
WHERE fa.feed_id = f.id
LIMIT 1
)
WHERE f.id = '13815'
based on several answers here, i found something that worked for me and i wanted to generalize and explain what's going on.
convert:
LEFT JOIN table2 t2 ON (t2.thing = t1.thing)
to:
LEFT JOIN table2 t2 ON (t2.p_key = (SELECT MIN(t2_.p_key)
FROM table2 t2_ WHERE (t2_.thing = t1.thing) LIMIT 1))
the condition that connects t1 and t2 is moved from the ON and into the inner query WHERE. the MIN(primary key) or LIMIT 1 makes sure that only 1 row is returned by the inner query.
after selecting one specific row we need to tell the ON which row it is. that's why the ON is comparing the primary key of the joined tabled.
you can play with the inner query (i.e. order+limit) but it must return one primary key of the desired row that will tell the ON the exact row to join.
Update - for MySQL 5.7+
another option relevant to MySQL 5.7+ is to use ANY_VALUE+GROUP BY. it will select an artist name that is not necessarily the first one.
SELECT feeds.*,ANY_VALUE(feeds_artists.name) artist_name
FROM feeds
LEFT JOIN feeds_artists ON feeds.id = feeds_artists.feed_id
GROUP BY feeds.id
more info about ANY_VALUE: https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
I've used something else (I think better...) and want to share it:
I created a VIEW that has a "group" clause
CREATE VIEW vCountries AS SELECT * PROVINCES GROUP BY country_code
SELECT * FROM client INNER JOIN vCountries on client_province = province_id
I want to say yet, that I think that we need to do this solution BECAUSE WE DID SOMETHING WRONG IN THE ANALYSIS... at least in my case... but sometimes it's cheaper to do this that to redesign everything...
I hope it helps!
Here is my answer using the group by clause.
SELECT *
FROM feeds f
LEFT JOIN
(
SELECT artist_id, feed_id
FROM feeds_artists
GROUP BY artist_id, feed_id
) fa ON fa.feed_id = f.id
LEFT JOIN artists a ON a.artist_id = fa.artist_id
I want to give a more generalized answer. One that will handle any case when you want to select only the first item in a LEFT JOIN.
You can use a subquery that GROUP_CONCATS what you want (sorted, too!), then just split the GROUP_CONCAT'd result and take only its first item, like so...
LEFT JOIN Person ON Person.id = (
SELECT SUBSTRING_INDEX(
GROUP_CONCAT(FirstName ORDER BY FirstName DESC SEPARATOR "_" ), '_', 1)
) FROM Person
);
Since we have DESC as our ORDER BY option, this will return a Person id for someone like "Zack". If we wanted someone with the name like "Andy", we would change ORDER BY FirstName DESC to ORDER BY FirstName ASC.
This is nimble, as this places the power of ordering totally within your hands. But, after much testing, it will not scale well in a situation with lots of users and lots of data.
It is, however, useful in running data-intensive reports for admin.
For some database like DB2 and PostgreSQL, you have to use the key word LATERAL for specifying a sub query in the LEFT JOIN : (here, it's for DB2)
SELECT f.*, a.*
FROM feeds f
LEFT JOIN LATERAL
(
SELECT artist_id, feed_id
FROM feeds_artists sfa
WHERE sfa.feed_id = f.id
fetch first 1 rows only
) fa ON fa.feed_id = f.id
LEFT JOIN artists a ON a.artist_id = fa.artist_id
I know this is not a direct solution but as I've faced this and it's always a huge problem for me, and also using left join select etc. sometimes lead to a heavy process cost in database and server, I prefer doing this kind of left joins using array in php like this:
First get the data in range from second table and while you need just one row from second table, just save them with left join in-common column as key in result array.
SQL1:
$sql = SELECT artist_id FROM feeds_artists fa WHERE fa.feed_id {...RANGE...}
$res = $mysqli->query($sql);
if ($res->num_rows > 0) {
while ($row = $res->fetch_assoc()) {
$join_data[...$KEY...] = $row['artist_id'];
}
Then, get the base data and add detail of left join table from previous array while fetch them like this:
SQL2:
$sql = SELECT * FROM feeds f WHERE f.id {...RANGE...};
$res = $mysqli->query($sql);
if ($res->num_rows > 0) {
while ($row = $res->fetch_assoc()) {
$key = $row[in_common_col_value];
$row['EXTRA_DATA'] = $join_data[$key];
$final_data[] = $row;
}
Now, you'll have a $final_data array with desire extra data from $join_data array. this usually works good for date range data and like this.
$query = mysqli_query($mysqli, "SELECT product_name ,MIN(product_price) product_price,link FROM(
select jumia.product_name,jumia.product_price,jumia.product_image,jumia.link from jumia INNER JOIN
jumia ON jumia.id = olx.id
where product.name like '%{$search}%'
UNION
select olx.product_name,olx.product_price,olx.product_image, olx.link from olx INNER JOIN
olx ON olx.id = jumia.id
where product.name like '%{$search}%')Minim
GROUP BY product_name,product_image
");
I am trying to create a query from two tables with similar column names as displayed above that will allow me to display the rows between the two tables that have the lowest price.
For example, if product_name called mattresses is searched the matching item in my database whose price is lower between the two table names should be displayed. Some help would be appreciated
I think this is the general idea of what you're trying to do:
SELECT id, price
FROM (SELECT id, price FROM T1 WHERE price = (SELECT min(price) FROM T1)
UNION
SELECT id, price FROM T2 WHERE price = (SELECT min(price) FROM T2)
) AS M
ORDER BY price ASC
LIMIT 1
Ended up changing the code and removing the ordering at the end and this finally worked. I hadn't properly linked my database using foreign keys, and changed my code to reflect this.
$query = mysqli_query($mysqli, "
SELECT product_name
, MIN(product_price) product_price
, link
FROM
( select j.product_name
, j.product_price
, j.link
from jumia j
JOIN olx
ON j.categoryID = olx.categoryID
where j.product_name like '%{$search}%'
UNION select olx.product_name
, olx.product_price
, olx.link
from olx
JOIN jumia
ON jumia.categoryID = olx.categoryID
where olx.product_name like '%{$search}%'
) x
");
Goal:
To have a table of SIC codes with their descriptions (joined via another table) and count the number of instances of each DISTINCT SIC code used. Ideal outcome below...followed by current mySQL select statement:
SIC Code | Description | Count of SIC Code
0001 | Desc A | 10
0002 | Desc B | 50
000N | Desc N | 80
Current SQL Statement, but causing error notice:
$sql = "SELECT DISTINCT company_list.SIC_Codes, sic_codes.SIC_desc, COUNT(*) as sicCount, FROM company_list LEFT JOIN sic_codes ON company_list.SIC_Codes = sic_codes.SIC_code;";
Any ideas or perspective on which count syntax to use to obtain the example above?
Thank you.
Try making a subquery for COUNT something like this.
$sql = "SELECT DISTINCT company_list.SIC_Codes, sic_codes.SIC_desc, (SELECT COUNT(*) FROM company_list LEFT JOIN sic_codes ON company_list.SIC_Codes = sic_codes.SIC_code) as sicCount, FROM company_list LEFT JOIN sic_codes ON company_list.SIC_Codes = sic_codes.SIC_code";
Instead of putting the COUNT as field, try making a subquery for it.
Change COUNT(*) to (SELECT COUNT(*) FROM company_list LEFT JOIN sic_codes ON company_list.SIC_Codes = sic_codes.SIC_code)
i have a table that contains some articles with it's own ID and shared SKU key.
I've tried to make the query with a left join and using group result to take all ids returned from the query.
My data structure is like that:
id - name - sku - ...
1 - felix - cat
2 - tom - cat - ...
3 - sylvester - cat - ...
4 - red - pen - ...
5 - blue - pen - ...
I tried to use this query:
SELECT * FROM `test`
[LEFT/RIGHT/INNER] JOIN
(
SELECT GROUP_CONCAT(DISTINCT id) AS idsgroup FROM `test` WHERE (attribute_name = 'sku') GROUP BY value_name LIMIT 0, 3
) bind
ON id IN (bind.idsgroup);
this query is wrong, it return only 1 id per group instead all ids selected from concat or in LEFT JOIN case, obviously all rows.
Any suggestion workaround to achieve the right result?
EDIT:
here a fiddle with the structure:
http://sqlfiddle.com/#!9/b6747a
And the query i tried into:
SELECT * FROM `view_test`
INNER JOIN
(
SELECT GROUP_CONCAT(DISTINCT entity_id) AS idsgroup FROM `view_test` WHERE (attribute_name = 'sku') GROUP BY value_name LIMIT 0, 3
) bind
ON entity_id IN (bind.idsgroup);
As this pic show, my result lost some ids, part of the group.
EDIT 2:
after i used FIND_IN_SET() suggested by Kickstart the result is the expected:
SELECT * FROM `view_test`
INNER JOIN
(
SELECT GROUP_CONCAT(DISTINCT entity_id) AS idsgroup FROM `view_test` WHERE (attribute_name = 'sku') GROUP BY value_name LIMIT 0, 3
) bind
ON FIND_IN_SET(entity_id, bind.idsgroup);
The simple fix would appear to be to use FIND_IN_SET for the join. But this is a bit of a hack and will not be that quick.
SELECT *
FROM `view_test`
INNER JOIN
(
SELECT GROUP_CONCAT(DISTINCT entity_id) AS idsgroup
FROM `view_test`
WHERE (attribute_name = 'sku')
GROUP BY value_name
LIMIT 0, 3
) bind
ON FIND_IN_SET(entity_id, bind.idsgroup);
Further not sure why you have a LIMIT on the sub query, especially without an order clause.
Possibly better to use a sub query to just get the DISTINCT entity_id with an attribute_name of sku and join against that.
SELECT *
FROM `view_test`
INNER JOIN
(
SELECT DISTINCT entity_id
FROM `view_test`
WHERE (attribute_name = 'sku')
) bind
ON view_test.entity_id = bind.entity_id
Something like this?
SELECT t.*, group_concat(t2.id) FROM `test` t
LEFT JOIN test t2 ON t2.attribute_name = 'sku' and t.id != t2.id
group by t.id;
row, and the list of all ids that have same SKU
Here is my query:
$result = $mysqli->query('SELECT DISTINCT SKU_SIZE_PART1
FROM SKU_DATA
ORDER BY SKU_SIZE_PART1 DESC');
Now this works perfect for SKU_SIZE_PART1 but I have 2 more parts that I need to grab. Now when I put a comma and do this: 'SKU_SIZE_PART1, SKU_SIZE_PART2, SKU_SIZE_PART3' then the DISTINCT doesn't work and I get a ton of duplicates, and then I'm not sure how to order the query so that all of them are ordered by the size and DESC.
Does that make sense? I could just duplicate that query 2 more times and have 3 separate queries but I would like to know how to accomplish this with just one.
I'm not positive that I understand what you're trying to do, but it sounds like you might actually want something like this:
SELECT SKU_SIZE_PART1 AS SKU_SIZE_PART
FROM SKU_DATA
UNION
SELECT SKU_SIZE_PART2 AS SKU_SIZE_PART
FROM SKU_DATA
UNION
SELECT SKU_SIZE_PART3 AS SKU_SIZE_PART
FROM SKU_DATA
ORDER BY SKU_SIZE_PART DESC
which will return all distinct SKU_SIZE_PART1/2/3 values in a single column, rather than all distinct (SKU_SIZE_PART1, SKU_SIZE_PART2, SKU_SIZE_PART3) triads in three columns.
After reading your question several times, I figured this might be what you are looking for:
SELECT SKU_SIZE_PART1 AS ssp
FROM SKU_DATA
UNION
SELECT SKU_SIZE_PART2 AS ssp
FROM SKU_DATA
UNION
SELECT SKU_SIZE_PART3 AS ssp
FROM SKU_DATA
ORDER BY ssp DESC
SELECT d.sku_size_part1, d.sku_size_part2, d.sku_size_part3
FROM sku_data d
WHERE d.id IN (
SELECT s.id <<--- replace `id` with the real primary-key for table `sku_data`
FROM sku_data s
GROUP BY s.sku_size_part1)
ORDER BY d.sku_size_part1 DESC
Note that this will select rows more or less at random.
Although all sku_size_parts will be from the same row, lots of values will be hidden.
If you want to make the query stable, you need to add a having clause in the inner subselect.
Something like this:
SELECT d.sku_size_part1, d.sku_size_part2, d.sku_size_part3
FROM sku_data d
WHERE d.id IN (
SELECT s.id <<--- replace `id` with the real primary-key for table `sku_data`
FROM sku_data s
GROUP BY s.sku_size_part1
HAVING s.sku_size_part2 = MIN(s.sku_size_part2)
AND s.sku_size_part3 = MIN(s.sku_size_part3))
ORDER BY d.sku_size_part1 DESC
Either that or you want #bfavaretto's UNION variant.
DISTINCT selects a distinct set of rows, not columns... the assumption/problem here is how to condense multiple columns. If you had the following table
sku1 | sku2 | sku3
---------------------
a | a | b
b | b | b
Telling it to select destinct would return both rows because none of them are distinct, you couldn't just remove the third column because then the row data would be inconsistent. If you want everything in one table you can do this with subqueries.
SELECT (SELECT DISTINCT SKU_SIZE_PART1 FROM SKU_DATA ORDER BY SKU_SIZE_PART1 DESC)
as part1, (SELECT DISTINCT SKU_SIZE_PART2 FROM SKU_DATA ORDER BY SKU_SIZE_PART2 DESC)
as part2, (SELECT DISTINCT SKU_SIZE_PART3 FROM SKU_DATA ORDER BY SKU_SIZE_PART1 DESC)
as part3 FROM SKU_DATA
You can read up a little on how DISTINCT works to see why you can't just do SELECT DISTINCT SKU_SIZE_PART1, PART2, PART3. Somewhere like This Link