I have this query which works fine. No errors or what soever. But it doesn't seem to work with last part and doesn't populate the last three fields from contact_data cd.
select c.*,cd.* FROM
(select * from
(select con.id_contact as v,substr(concat(ifnull(con.firstname,''),ifnull(con.lastname,'')),1,4) as s0, null as s1, null as s2
from contact con where id_user=123 and firstname != '' and firstname != ' ' and firstname is not null) as tbl235768 where 1=1 AND v IN
(select v from
(select * from
(select con.id_contact as v,substr(concat(ifnull(con.firstname,''),ifnull(con.lastname,'')),1,4) as s0, null as s1, null as s2
from contact con where id_user=123 and email = '') as tbl235770 where 1=1) as tblAnd) AND v IN
(select v from
(select * from (select con.id_contact as v,substr(concat(ifnull(con.firstname,''),ifnull(con.lastname,'')),1,4) as s0, null as s1, null as s2
from contact con where id_user=123 and mobile != '' and mobile != ' ' and mobile is not null) as tbl235772 where 1=1) as tblAnd)) as tblBucket
left join contact c on c.id_contact=v left join
(select id_contact, group_concat(name) as custom_names,group_concat(value) as custom_values FROM contact_data where
id_user=20 group by id_contact) as cd on cd.id_contact=c.id_contact group by (c.id_contact)
But, if i run the last part only which is the following
select id_contact, group_concat(name) as custom_names,group_concat(value) as custom_values
FROM contact_data where id_user=798 group by id_contact
It gives me desired result. what is wrong with my query? Any help would be greatly apprecited, Thanks.
Edit : i am editing my question after getting a few answers.
I have removed all the nested parts but still no luck.
select c.*,cd.* FROM (select * from (select con.id_contact as v,substr(concat(ifnull(con.firstname,''),ifnull(con.lastname,'')),1,4) as s0, null as s1, null as s2 from contact con where id_user=10879) as tbl235785
where 1=1) as tblBucket left join contact c on c.id_contact=v left join (select id_contact, group_concat(name) as custom_names,group_concat(value) as custom_values
FROM contact_data where id_user=798 group by id_contact) as cd on cd.id_contact=c.id_contact group by (c.id_contact)
Here is your sql made simpler:
All of the 1=1 and SELECT * FROM (sub-query) can go away -- they don't do anything. Then the logic becomes clear -- you just need some additional clauses on your where statement instead of the sub-queries. I think when it is cleaned up like this you can see - in the first select you have id_user=123 and in the second you have id_user=20.
Probably want those to be the same? You don't even need the need the where clause in the 2nd joined select. Since it is joined you should just join this field the value in the outer query.
select c.*,cd.*
FROM (select con.id_contact as v,substr(concat(ifnull(con.firstname,''),ifnull(con.lastname,'')),1,4) as s0, null as s1, null as s2
from contact con
where id_user=123 and
firstname != '' and firstname != ' ' and firstname is not null
and
email = ''
OR
(
mobile != '' and mobile != ' ' and mobile is not null
)
) as tbl235768
left join contact c on c.id_contact=v
left join (select id_contact, group_concat(name) as custom_names,group_concat(value) as custom_values
FROM contact_data
where id_user=20
group by id_contact
) as cd on cd.id_contact=c.id_contact
group by (c.id_contact)
In one place you are matching in id_user=10879 and then in different sub query you are matching on id_user=798, then you join the two and since the id_user fields are both looking at different numbers, you get no matches on your join.
Related
The goal here is to:
1. Fetch the row with the most recent date from EACH store for EACH ingredient.
2. From this result, compare the prices to find the cheapest store for EACH ingredient.
I can accomplish either the first or second goal in separate queries, but not in the same.
How can i filter out a selection and then apply another filter on the previous result?
EDIT:
I've been having problems with results that i get from MAX and MIN since it just fetches the rest of the data arbitrarily. To avoid this im supposed to join tables on multiple columns (i guess). Im not sure how this will work with duplicate dates etc.
I've included an image of a query and its output data.
If we use ingredient1 as an example, it exists in three separate stores (in one store twice on different dates).
In this case the cheapest current price for ingredient1 would be store3. If the fourth row dated 2013-05-25 was even cheaper, it would still not "win" due to it being out of date.
(Disregard brandname, they dont really matter in this problem.)
Would appreciate any help/input you can offer!
This question is really interesting!
So, first, we get the row with the most recent date from EACH store for EACH ingredient. (It is possible that the most recent dates from each store can be different.)
Then, we compare the prices from each store (regardless of the date) to find the least price for each ingredient.
The query below uses the GROUP_CONCAT function in good measure. Here's a SO question regarding the use of the function.
SELECT
i.name as ingredient_name
, MIN(store_price.price) as price
, SUBSTRING_INDEX(
GROUP_CONCAT(store_price.date ORDER BY store_price.price),
',',
1
) as date
, SUBSTRING_INDEX(
GROUP_CONCAT(s.name ORDER BY store_price.price),
',',
1
) as store_name
, SUBSTRING_INDEX(
GROUP_CONCAT(b.name ORDER BY store_price.price),
',',
1
) as brand_name
FROM
ingredient i
JOIN
(SELECT
ip.ingredient_id as ingredient_id
, stip.store_id as store_id
, btip.brand_id as brand_id
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.ingredient_price_id ORDER BY ip.date DESC),
',',
1
), UNSIGNED INTEGER) as ingredient_price_id
, MAX(ip.date) as date
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.price ORDER BY ip.date DESC),
',',
1
), DECIMAL(5,2)) as price
FROM ingredient_price ip
JOIN store_to_ingredient_price stip ON ip.ingredient_price_id = stip.ingredient_price_id
JOIN brand_to_ingredient_price btip ON ip.ingredient_price_id = btip.ingredient_price_id
GROUP BY
ip.ingredient_id
, stip.store_id) store_price
ON i.ingredient_id = store_price.ingredient_id
JOIN store s ON s.store_id = store_price.store_id
JOIN brand b ON b.brand_id = store_price.brand_id
GROUP BY
store_price.ingredient_id;
You can check the implementation on this SQL Fiddle.
The version below, which ignores the brand, is slightly smaller:
SELECT
i.name as ingredient_name
, MIN(store_price.price) as price
, SUBSTRING_INDEX(
GROUP_CONCAT(store_price.date ORDER BY store_price.price),
',',
1
) as date
, SUBSTRING_INDEX(
GROUP_CONCAT(s.name ORDER BY store_price.price),
',',
1
) as store_name
FROM
ingredient i
JOIN
(SELECT
ip.ingredient_id as ingredient_id
, stip.store_id as store_id
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.ingredient_price_id ORDER BY ip.date DESC),
',',
1
), UNSIGNED INTEGER) as ingredient_price_id
, MAX(ip.date) as date
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.price ORDER BY ip.date DESC),
',',
1
), DECIMAL(5,2)) as price
FROM ingredient_price ip
JOIN store_to_ingredient_price stip ON ip.ingredient_price_id = stip.ingredient_price_id
GROUP BY
ip.ingredient_id
, stip.store_id) store_price
ON i.ingredient_id = store_price.ingredient_id
JOIN store s ON s.store_id = store_price.store_id
GROUP BY
store_price.ingredient_id;
References:
Simulating First/Last aggregate functions in MySQL
This probably needs a couple of sub queries joined together.
This isn't tested (as I don't have your table definitions, nor any test data), but something like this:-
SELECT i.name AS ingredient,
ip.price,
ip.date,
s.name AS storename,
b.name AS brandname
FROM ingredient i
INNER JOIN ingredient_price ip
ON ingredient.ingredient_id = ingredient_price.ingredient_id
INNER JOIN store_to_ingredient_price stip
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
INNER JOIN store s
ON store_to_ingredient_price.store_id = store.store_id
INNER JOIN brand_to_ingredient_price btip
ON ingredient_price.ingredient_price_id = brand_to_ingredient_price.ingredient_price_id
INNER JOIN brand b
ON brand_to_ingredient_price.brand_id = brand.brand_id
INNER JOIN
(
SELECT i.ingredient_id,
stip.store_id,
ip.date,
MIN(ip.price) AS lowest_price
FROM ingredient i
INNER JOIN ingredient_price ip
ON ingredient.ingredient_id = ingredient_price.ingredient_id
INNER JOIN store_to_ingredient_price stip
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
INNER JOIN
(
SELECT i.ingredient_id,
stip.store_id,
MAX(ip.date) AS latest_date
FROM ingredient i
INNER JOIN ingredient_price ip
ON ingredient.ingredient_id = ingredient_price.ingredient_id
INNER JOIN store_to_ingredient_price stip
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
GROUP BY ingredient_id, store_id
) Sub1
ON i.ingredient_id = Sub1.ingredient_id
AND stip.store_id = Sub1.store_id
AND ip.date = Sub1.latest_date
GROUP BY i.ingredient_id, stip.store_id, ip.date
) Sub2
ON i.ingredient_id = Sub2.ingredient_id
AND stip.store_id = Sub2.store_id
AND ip.date = Sub2.date
AND ip.price = Sub2.lowest_price
Try this:
SELECT `newest`.ingredient, `newest`.store,
`newest`.brand, `newest`.price, `newest`.`latest_date`
FROM
(SELECT ingredient.name AS ingredient, store.name AS store,
brand.name AS brand, ingredient_price.price,
MAX( ingredient_price.date ) AS `latest_date`
FROM ingredient
LEFT OUTER JOIN ingredient_price
ON ingredient.ingredient_id = ingredient_price.ingredient_id
LEFT OUTER JOIN store_to_ingredient_price
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
LEFT OUTER JOIN store
ON store_to_ingredient_price.store_id = store.store_id
LEFT OUTER JOIN brand_to_ingredient_price
ON ingredient_price.ingredient_price_id = brand_to_ingredient_price.ingredient_price_id
LEFT OUTER JOIN brand
ON brand_to_ingredient_price.brand_id = brand.brand_id
GROUP BY ingredient.name) `newest`
ORDER BY `newest`.price
LIMIT 1
I'm trying to accommodate a similar solution to this one - what I have is a SELECT query inside a JOIN, and the problem is that the query runs at full for all rows (I'm talking 60,000 rows per table - and it runs on 3 tables!).
So what I want to do, is add a WHERE clause to the SELECTs inside the JOIN.
But, I can't access the outer SELECT and get the proper WHERE condition I need.
The query I'm attempting is here:
SELECT c.compete_id AS id,
s.id AS store_id,
c.enabled AS enabled,
s.store_name AS store_name,
s.store_url AS store_url,
c.verified AS verified,
r.rating_total AS rating,
r.positive_percent AS percent,
r.type AS type
FROM compete_settings c
LEFT JOIN stores s
ON c.compete_id = s.id
LEFT JOIN (
(SELECT store_id, rating_total, positive_percent, 'ebay' AS type FROM ebay_sellers WHERE store_id = c.compete_id)
UNION
(SELECT store_id, rating_total, positive_percent, 'amazon' AS type FROM amazon_sellers WHERE store_id = c.compete_id)
UNION
(SELECT store_id, CASE WHEN rank = 0 THEN NULL ELSE (200000 - rank) END AS rating_total, '100' as positive_percent, 'alexa' AS type FROM alexa_ratings WHERE store_id = c.compete_id)
) AS r
ON c.compete_id = r.store_id
WHERE c.store_id = :store_id
Note, :store_id is a variable bound through the framework - let's imagine it's the number 12345.
How can I do this? Any ideas?
We ended up going witha different approach - we just JOINed everything and only selected the right columns with a CASE. Here's the final query:
SELECT c.id AS id,
s.id AS store_id,
c.enabled AS enabled,
s.store_name AS store_name,
s.store_url AS store_url,
c.verified AS verified,
(CASE WHEN eb.rating_total IS NOT NULL THEN eb.rating_total
WHEN am.rating_total IS NOT NULL THEN am.rating_total
WHEN ax.rank IS NOT NULL THEN ax.rank
END) AS rating,
(CASE WHEN eb.positive_percent IS NOT NULL THEN eb.positive_percent
WHEN am.positive_percent IS NOT NULL THEN am.positive_percent
WHEN ax.rank IS NOT NULL THEN '100'
END) AS percent,
(CASE WHEN eb.positive_percent IS NOT NULL THEN 'ebay'
WHEN am.positive_percent IS NOT NULL THEN 'amazon'
WHEN ax.rank IS NOT NULL THEN 'alexa'
END) AS type
FROM compete_settings c
LEFT JOIN stores s
ON c.compete_id = s.id
LEFT JOIN ebay_sellers eb ON c.compete_id = eb.store_id
LEFT JOIN amazon_sellers am ON c.compete_id = am.store_id
LEFT JOIN alexa_ratings ax ON c.compete_id = ax.store_id
WHERE c.store_id = :store_id
I am trying to bring back a string based on an IF statement but it is extremely slow.
It has something to do with the first subquery but I am unsure of how to rearrange this as to bring back the same results but faster.
Here is my SQL:
SELECT IF
(
(
SELECT COUNT(*)
FROM
(
SELECT DISTINCT enquiryId, type
FROM parts_enquiries, parts_service_types AS pst
WHERE parts_enquiries.serviceTypeId = pst.id
) AS parts
WHERE parts.enquiryId = enquiries.id
) > 1, 'Mixed',
(
SELECT DISTINCT type
FROM parts_enquiries, parts_service_types AS pst
WHERE parts_enquiries.serviceTypeId = pst.id AND enquiryId = enquiries.id
)
) AS partTypes
FROM enquiries,
entities
WHERE enquiries.entityId = entities.id
How can I make it faster?
I have modified my original query below, but I am getting the error that subquery returns more than one row:
SELECT
(SELECT
CASE WHEN COUNT(DISTINCT type) > 1 THEN 'Mixed' ELSE `type` END AS type
FROM parts_enquiries
INNER JOIN parts_service_types AS pst ON parts_enquiries.serviceTypeId = pst.id
INNER JOIN enquiries ON parts_enquiries.enquiryId = enquiries.id
INNER JOIN entities ON enquiries.entityId = entities.id
GROUP BY enquiryId) AS partTypes
FROM enquiries,
entities
WHERE enquiries.entityId = entities.id
Please have a look if this query yields the same results:
SELECT
enquiryId,
CASE WHEN COUNT(DISTINCT type) > 1 THEN 'Mixed' ELSE `type` END AS type
FROM parts_enquiries
INNER JOIN parts_service_types AS pst ON parts_enquiries.serviceTypeId = pst.id
INNER JOIN enquiries ON parts_enquiries.enquiryId = enquiries.id
INNER JOIN entities ON enquiries.entityId = entities.id
GROUP BY enquiryId
But N.B.'s comment is still valid. To see if and index is used and other information we need to see the EXPLAIN and the table definitions.
This should get you what you want.
I would first pre-query your parts enquiries and parts service types looking for both the count and MINIMUM of the part 'type', grouped by the enquiry ID.
then, run your IF() against that result. If the distinct count is > 0, then 'Mixed'. If only one, since I did the MIN(), it would only have the description of that one value that you desire anyhow.
SELECT
E.ID
IF ( PreQuery.DistTypes > 1, 'Mixed', PreQuery.FirstType ) as PartType
from
Enquiries E
JOIN ( SELECT
PE.EnquiryID,
COUNT( DISTINCT PE.ServiceTypeID ) as DistTypes,
MIN( PST.Type ) as FirstType
from
Parts_Enquiries PE
JOIN Parts_Service_Types PST
ON PE.ServiceTypeID = PST.ID
group by
PE.EnquiryID ) as PreQuery
ON E.ID = PreQuery.EnquiryID
I have the below SQL query and it will return a group_name along with a list of departments for that group. I was able to lowercase the departments returned, but I can't figure out how to lowercase the group name as well.
Also, instead of lowercasing each returned column is there perhaps a way to lowercase the entire result in one swoop?
SELECT sg.group_name,A.dept_name
FROM `sys_groups` `sg`
INNER JOIN (SELECT gda.group_id,
GROUP_CONCAT(LOWER(sd.dept_name) ORDER BY `dept_name`
SEPARATOR '|'
) `dept_name`
FROM `group_dept_access` `gda`
INNER JOIN `sys_department` `sd`
ON gda.dept_id = sd.dept_id
GROUP BY gda.group_id) AS `A`
ON sg.group_id = A.group_id
Thank you in advance!
Try this:
SELECT LOWER(sg.group_name) group_name, LOWER(A.dept_name) dept_name
FROM sys_groups sg
INNER JOIN (SELECT gda.group_id,
GROUP_CONCAT(sd.dept_name ORDER BY dept_name SEPARATOR '|') dept_name
FROM group_dept_access gda
INNER JOIN sys_department sd ON gda.dept_id = sd.dept_id
GROUP BY gda.group_id
) AS A ON sg.group_id = A.group_id
Well for whatever reason MySQL does not support subqueries in a View. I'm stuck using MySQL for the forseeable future and I'm needing to write a View to aggregate data from an EAV-style table into a more readable format. The View itself is going to act as the Model for a Ruby on Rails-based application (it's read only so I'm safe using a view).
How could I, if I can at all, rewrite this beast to be able to create a view out of it? I suppose if I had to I could just create a series of mini-views and then one master view (one View to rule them all, as the case could be) but I'm wondering there's another way.
Here's the monster; the large numbers of outer joins are because all of the fields are optional but I need to display all of the columns:
select ls.subscriberid as id, l.name as list_name,
ifnull(cfn.first_name, '') as first_name,
ifnull(cln.last_name, '') as last_name,
ifnull(ce.email_address, '') as email_address,
ifnull(cm.mobile_phone, '') as mobile_phone
from (
select ls.subscriberid, ifnull(s.data, '') as first_name
from subscribers_data s
right outer join list_subscribers ls on ls.subscriberid = s.subscriberid
where s.fieldid = 2 /* First Name */
) cfn
right outer join (
select ls.subscriberid, ifnull(s.data, '') as last_name
from subscribers_data s
right outer join list_subscribers ls on ls.subscriberid = s.subscriberid
where s.fieldid = 3 /* Last Name */
) cln on cfn.subscriberid = cln.subscriberid
right outer join (
select ls.subscriberid, ls.emailaddress as email_address
from list_subscribers ls
) ce on ce.subscriberid = cfn.subscriberid
right outer join (
select ls.subscriberid, ifnull(s.data, '') as mobile_phone
from subscribers_data s
right outer join list_subscribers ls on ls.subscriberid = s.subscriberid
where s.fieldid = 81 /* Mobile Phone */
) cm on cm.subscriberid = cfn.subscriberid
right outer join list_subscribers ls on ls.subscriberid = cfn.subscriberid
inner join lists l on ls.listid = l.listid
try this one:
SELECT ls.subscriberid as id, l.name as list_name,
ifnull(group_concat(CASE WHEN s.fieldId = 2
THEN s.data
ELSE NULL
END), '') as first_name,
ifnull(group_concat(CASE WHEN s.fieldId = 3
THEN s.data
ELSE NULL
END), '') as last_name,
ifnull(ls.email_address, '') as email_address,
ifnull(group_concat(CASE WHEN s.fieldId = 81
THEN s.data
ELSE NULL
END), '') as mobile_phone
FROM list_subscribers ls
INNER JOIN lists l on ls.listid = l.listid
LEFT JOIN subscribers_data s on ls.subscriberid = s.subscriberid
GROUP BY ls.subscriberid, l.name, ls.email_address