MySQL Append results from query to current record? - mysql

I have a successful query that currently returns all data except for one table. The last table contains meta data where it is a one to many relationship (one product, multiple records with related data).
Is there a way to append a field with a value to the end of an existing query so that I can still access the record set in PHP with a mysql_fetch_array() command?
essentially I want to query for any records with the product ID, and then append multiple additional fields.
After thinking about it i was actually wondering if I can include an array within the records set (i.e. the array of results for the meta data) and access that through PHP?
Working Query:
SELECT offers.*, c.short_name AS sponsorName, c.logo AS sponsorLogo
FROM offers LEFT JOIN sponsors AS c ON c.id=offers.sponsor ORDER BY end_date ASC
Results:
ID: 43875
category: 1
state: CO
city: Denver
zip: 80221
sponsor: 1
title: The coolest thing ever
duration: 2 years
price: 10
frequency:: Month
Second query that gets the right meta data-
SELECT mo.`name` AS meta_value FROM offer_metas
LEFT JOIN meta_options AS mo ON mo.id = offer_metas.meta_option
WHERE offer_id='48735' ORDER BY meta_option ASC
results:
meta_value:
'5-10 tickets'
'General Admission'
I want to add those two fields to the record up top .. but don't know how to append all fields within a query result into a single already existing record.
--SOLVED--
Query has been adjusted to account for GROUP_CONCAT as follows
SELECT
offers.*,
s.short_name AS sponsorName,
s.logo AS sponsorLogo,
GROUP_CONCAT( mn.title) titles,
GROUP_CONCAT( mo.`name`) metas
FROM offers
LEFT JOIN sponsors AS s ON s.id = offers.sponsir
INNER JOIN offer_metas ON offer_metas.offer_id = offers.id
INNER JOIN meta_options as mo ON offer_metas.meta_option = mo.id
INNER JOIN meta_names as mn ON mo.category = mn.category AND mo.cat_seq = mn.seq
ORDER BY end_date ASC
data results looked good including 2 fields called "titles" and "metas" that looked like this
titles: 'Number of Tickets,Purchased Seats'
metas: '5-10,General Administration'
now THOSE .. I can work with in PHP .. and since they have the same number of elements i'll just parse them into an array i can better work with :)

Further to your comments above, it sounds like GROUP_CONCAT() will give you what you're after. It won't return the meta_values as an array, but rather as a string (e.g. with each value separated by <br/> in order that they can be emitted directly as HTML):
SELECT
offers.*,
c.short_name AS sponsorName,
c.logo AS sponsorLogo,
GROUP_CONCAT(mo.name ORDER BY meta_option ASC SEPARATOR '<br/>') AS meta_values
FROM
offers
LEFT JOIN sponsors AS c ON c.id = offers.sponsor
LEFT JOIN offer_metas AS o ON o.offer_id = offers.id
LEFT JOIN meta_options AS mo ON mo.id = offer_metas.meta_option
ORDER BY end_date ASC;
Note that, if you need to escape the meta values for any HTML they might contain, you'll need to choose a different separator (perhaps U+001E, INFORMATION SEPARATOR TWO?) and have PHP replace that separator with suitable HTML after escaping any contained HTML.

Related

MySQL Inner join naming error?

http://sqlfiddle.com/#!9/e6effb/1
I'm trying to get a top 10 by revenue per brand for France on december.
There are 2 tables (first table has date, second table has brand and I'm trying to join them)
I get this error "FUNCTION db_9_d870e5.SUM does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual"
Is my use of Inner join there correct?
It's because you had an extra space after SUM. Please change it from
SUM (o1.total_net_revenue)to SUM(o1.total_net_revenue).
See more about it here.
Also after correcting it, your query still had more error as you were not selecting order_id on your intermediate table i2 so edited here as :
SELECT o1.order_id, o1.country, i2.brand,
SUM(o1.total_net_revenue)
FROM orders o1
INNER JOIN (
SELECT i1.brand, SUM(i1.net_revenue) AS total_net_revenue,order_id
FROM ordered_items i1
WHERE i1.country = 'France'
GROUP BY i1.brand
) i2
ON o1.order_id = i2.order_id AND o1.total_net_revenue = i2.total_net_revenue
AND o1.total_net_revenue = i2.total_net_revenue
WHERE o1.country = 'France' AND o1.created_at BETWEEN '2016-12-01' AND '2016-12-31'
GROUP BY 1,2,3
ORDER BY 4
LIMIT 10`
--EDIT stack Fan is correct that the o2.total_net_revenue exists. My confusion was because the data structure duplicated three columns between the tables, including one that was being looked for.
There were a couple errors with your SQL statement:
1. You were referencing an invalid column in your outer-select-SUM function. I believe you're actually after i2.total_net_revenue.
The table structure is terrible, the "important" columns (country, revenue, order_id) are duplicated between the two tables. I would also expect the revenue columns to share the same name, if they always have the same values in them. In the example, there's no difference between i1.net_revenue and o1.total_net_revenue.
In your inner join, you didn't reference i1.order_id, which meant that your "on" clause couldn't execute correctly.
PROTIP:
When you run into an issue like this, take all the complicated bits out of your query and get the base query working correctly first. THEN add your functions.
PROTIP:
In your GROUP BY clause, reference the actual columns, NOT the column numbers. It makes your query more robust.
This is the query I ended up with:
SELECT o1.order_id, o1.country, i2.brand,
SUM(i2.total_net_revenue) AS total_rev
FROM orders o1
INNER JOIN (
SELECT i1.order_id, i1.brand, SUM(i1.net_revenue) AS total_net_revenue
FROM ordered_items i1
WHERE i1.country = 'France'
GROUP BY i1.brand
) i2
ON o1.order_id = i2.order_id AND o1.total_net_revenue = i2.total_net_revenue
AND o1.total_net_revenue = i2.total_net_revenue
WHERE o1.country = 'France' AND o1.created_at BETWEEN '2016-12-01' AND '2016-12-31'
GROUP BY o1.order_id, o1.country, i2.brand
ORDER BY total_rev
LIMIT 10

JOIN on multiple tables giving duplicate records - MySql

Here is list of my tables and necessary columns
users u .
screen_name,
country,
status
twitter_users_relationship tf. This table have multiple target_screen_name for each screen_name.
screen_name,
target_screen_name,
target_country,
follow_status
user_twitter_action_map ta
screen_name,
action_name,
action_status
user_targeted_countries utc .This table have multiple countries for each screen_name
screen_name,
country_name
I want to get all target_screen_name from twitter_users_relationship that have matched target_country with u.country or utc.country_name
My query so far
SELECT u.screen_name,
u.country,
tf.target_screen_name,
tf.target_country,
ta.action_name,
ta.action_status,
utc.country_name
FROM users u
LEFT JOIN twitter_users_relationship tf
ON u.screen_name=tf.screen_name
LEFT JOIN user_twitter_action_map ta
ON u.screen_name=ta.screen_name
AND ta.action_name='follow'
AND ta.action_status='active'
LEFT JOIN user_targeted_countries utc
ON u.screen_name= utc.screen_name
WHERE u.status = 'active'
AND tf.follow_status = 'pending'
AND tf.target_country != ''
AND tf.target_country IS NOT NULL
AND ( utc.country_name=tf.target_country OR u.country=tf.target_country)
AND u.screen_name = 'my_screen_name';
But this query giving me duplicate record for each entry of countries in user_targeted_countries. If there are 3 counties in user_targeted_countries the it will return 3 duplicate records.
Please let me know what JOIN I need to use with user_targeted_countries to get desired results.
u.country can be different than countries in utc.country_name
UPDATE -
If I removes OR u.country=tf.target_country from the WHERE clause then I get all the matched target_screen_name without duplicate. But I am not sure how to get all those records also that matches with u.country=tf.target_country ?
Depends on the business logic required ..
First, regardless to the question, your query is wrong(Either the LEFT JOIN or the conditions) . When using LEFT JOIN , conditions on the right table should only be specified in the ON clause, which means you need to move all the conditions on tf. and utc. to the ON clause.
Secondly, you can use a GROUP BY clause and choose one of the utc.country_name (different answers will be if you want a specific one, if it doesn't matter, use MAX() on this column).

MySQL - Trying to show results for rows that have 0 records...across 3 columns

There's a lot of Q&A out there for how to make MySQL show results for rows that have 0 records, but they all involve 1-2 tables/fields at most.
I'm trying to achieve the same ends, but across 3 fields, and I just can't seem to get it.
Here's what I've hacked together:
SELECT circuit.circuit_name, county.county_name, result.adr_result, count( result.adr_result ) AS num_results
FROM
(
SELECT cases.case_id, cases.county_id, cases.result_id
FROM cases
WHERE cases.status_id <> "2"
) q1
RIGHT JOIN county ON q1.county_id = county.county_id
RIGHT JOIN circuit ON county.circuit_id = circuit.circuit_id
RIGHT JOIN result ON q1.result_id = result.result_id
GROUP BY adr_result, circuit_name, county_name
ORDER BY circuit_name, county_name, adr_result
What I need to see is a list of ALL circuits in the first column, a list of ALL counties per circuit in the second column, a list of ALL possible adr_result entries for each county (they're the same for every county) in the third column, and then the respective count for the circuit/county/result combination-- even if it is 0. I've tried every combination of left, right and inner join (I know inner is definitely not the solution, but I'm frustrated) and just can't see where I'm going wrong.
Any help would be appreciated!
Here is a start. I can't follow your problem statement completely. For instance, what is the purposes of the cases table? None the less, when you say "ALL" records for each of those tables, I interpret it as a Cartesian product - which is implemented through the derived table in the FROM clause (notice the lack of the JOIN in that clause)
SELECT everthingjoin.circuit_name
, everthingjoin.county_name
, everthingjoin.adr_result
, COUNT(result.adr_result) AS num_results
FROM
(SELECT circuit.circuit_name, county.county_name, result.adr_result,
FROM circuit
JOIN county
JOIN result) AS everthingjoin
LEFT JOIN cases
ON cases.status_id <> "2"
AND cases.county_id = everthingjoin.county_id
LEFT JOIN circuit
ON everthingjoin.circuit_id = circuit.circuit_id
LEFT JOIN result
ON cases.result_id = result.result_id
GROUP BY adr_result, circuit_name, county_name
ORDER BY circuit_name, county_name, adr_result
try this, see if it provides some ideas:
SELECT
circuit.circuit_name
, county.county_name
, result.adr_result
, ISNULL(COUNT(result.*)) AS num_results
, COUNT(DISTINCT result.adr_result) AS num_distinct_results
FROM cases
LEFT JOIN county
ON cases.county_id = county.county_id
LEFT JOIN circuit
ON county.circuit_id = circuit.circuit_id
LEFT JOIN result
ON cases.result_id = result.result_id
WHERE cases.status_id <> "2"
GROUP BY
circuit.circuit_name
, county.county_name
, result.adr_result
ORDER BY
circuit_name, county_name, adr_result

COUNT evaluate to zero if no matching records

Take the following:
SELECT
Count(a.record_id) AS newrecruits
,a.studyrecord_id
FROM
visits AS a
INNER JOIN
(
SELECT
record_id
, MAX(modtime) AS latest
FROM
visits
GROUP BY
record_id
) AS b
ON (a.record_id = b.record_id) AND (a.modtime = b.latest)
WHERE (((a.visit_type_id)=1))
GROUP BY a.studyrecord_id;
I want to amend the COUNT part to display a zero if there are no records since I assume COUNT will evaluate to Null.
I have tried the following but still get no results:
IIF(ISNULL(COUNT(a.record_id)),0,COUNT(a.record_id)) AS newrecruits
Is this an issue because the join is on record_id? I tried changing the INNER to LEFT but also received no results.
Q
How do I get the above to evaluate to zero if there are no records matching the criteria?
Edit:
To give a little detail to the reasoning.
The studies table contains a field called 'original_recruits' based on activity before use of the database.
The visits tables tracks new_recruits (Count of records for each study).
I combine these in another query (original_recruits + new_recruits)- If there have been no new recruits I still need to display the original_recruits so if there are no records I need it to evalulate to zero instead of null so the final sum still works.
It seems like you want to count records by StudyRecords.
If you need a count of zero when you have no records, you need to join to a table named StudyRecords.
Did you have one? Else this is a nonsense to ask for rows when you don't have rows!
Let's suppose the StudyRecords exists, then the query should look like something like this :
SELECT
Count(a.record_id) AS newrecruits -- a.record_id will be null if there is zero count for a studyrecord, else will contain the id
sr.Id
FROM
visits AS a
INNER JOIN
(
SELECT
record_id
, MAX(modtime) AS latest
FROM
visits
GROUP BY
record_id
) AS b
ON (a.record_id = b.record_id) AND (a.modtime = b.latest)
LEFT OUTER JOIN studyrecord sr
ON sr.Id = a.studyrecord_id
WHERE a.visit_type_id = 1
GROUP BY sr.Id
I solved the problem by amending the final query where I display the result of combining the original and new recruits to include the IIF there.
SELECT
a.*
, IIF(IsNull([totalrecruits]),consents,totalrecruits)/a.target AS prog
, IIf(IsNull([totalrecruits]),consents,totalrecruits) AS trecruits
FROM
q_latest_studies AS a
LEFT JOIN q_totalrecruitment AS b
ON a.studyrecord_id=b.studyrecord_id
;

Comparing two values from the same select query

I have a select query which selects all products from my inventory table and joins them with two other tables (tables l_products and a_products)
SELECT
i.*,
b.title,
ROUND((i.price/100*80) - l.price,2) AS margin,
l.price AS l_price,
a.price AS a_price,
ROUND((a.price/100*80) - l.price, 2) AS l_margin
FROM inventory i
LEFT JOIN products b ON i.id = b.id
LEFT JOIN a_products a ON i.id = a.id
LEFT JOIN l_products l ON i.id = l.id
WHERE
a.condition LIKE IF(i.condition = 'New', 'New%', 'Used%')
AND l.condition LIKE IF(i.condition = 'New', 'New%', 'Used%')
This select query will normally give me a table such as...
id, title, condition, margin, l_price, a_price ...
001-new ... new 10 20 10
001-used ... used 10 25 20
002....
Now I need a condition in the query which will ignore all used products that are more expensive (have a higher a_price) than their 'new' counterparts, such as in the example above you can see that 001-used has a higher a_price than 001-new.
How can I achieve this with out having to resolve to using php
FULL JOIN this query with it self on a column which has a uniquely same value for each id prefix.
You may achieve this effect by adding another field to your SELECT call which produces same unique value for 001-new and 001-used, 002-new and 002-used...
Such value generation can be done by defining your own SQL Routine to extract first 3 characters from a column.