I have a multi-table SQL query.
My need is: The query should I generate a single line by 'etablissement_id' ... and all information that I want to be back in the same query.
The problem is that this query is currently on a table where "establishment" may have "multiple photos" and suddenly, my query I currently generates several lines for the same id...
I want the following statement - LEFT JOINetablissementContenuMultimediaON etablissement.etablissement_id = etablissementContenuMultimedia.etablissementContenuMultimedia_etablissementId - only a single multimedia content is displayed. Is it possible to do this in the query below?
Here is the generated query.
SELECT DISTINCT `etablissement`. * , `etablissementContenuMultimedia`. * , `misEnAvant`. * , `quartier`. *
FROM `etablissement`
LEFT JOIN `etablissementContenuMultimedia` ON etablissement.etablissement_id = etablissementContenuMultimedia.etablissementContenuMultimedia_etablissementId
LEFT JOIN `misEnAvant` ON misEnAvant.misEnAvant_etablissementId = etablissement.etablissement_id
LEFT JOIN `quartier` ON quartier_id = etablissement_quartierId
WHERE (
misEnAvant_typeMisEnAvantId =1
AND (
misEnAvant_dateDebut <= CURRENT_DATE
AND CURRENT_DATE <= misEnAvant_dateFin
)
)
AND (
etablissement_isActive =1
)
ORDER BY `etablissement`.`etablissement_id` ASC
LIMIT 0 , 30
Here is the code used ZF
public function find (){
$db = Zend_Db_Table::getDefaultAdapter();
$oSelect = $db->select();
$oSelect->distinct()
->from('etablissement')
->joinLeft('etablissementContenuMultimedia', 'etablissement.etablissement_id = etablissementContenuMultimedia.etablissementContenuMultimedia_etablissementId')
->joinLeft('misEnAvant', 'misEnAvant.misEnAvant_etablissementId = etablissement.etablissement_id')
->joinLeft('quartier', 'quartier_id = etablissement_quartierId ')
->where ('misEnAvant_typeMisEnAvantId = 1 AND (misEnAvant_dateDebut <= CURRENT_DATE AND CURRENT_DATE <= misEnAvant_dateFin) ')
->where ('etablissement_isActive = 1')
->order(new Zend_Db_Expr('RAND()'));
$zSql = $oSelect->__toString();
if(isset($_GET['debug']) AND $_GET['debug'] == 1)
echo $zSql ;
//die();
$oResultEtablissement = $db->fetchAll($oSelect);
return $oResultEtablissement ;
}
Can you help me?
Sincerely,
If you are looking to have only one of the media displayed out of many regardless of which it may be then you can just add a limit to the query? After that you can tweak the query for ASCending or DESCending perhaps?
Is this query supposed to have images (or image as it were) for one establishment, or one image each for each active establishment? I see you have a limit 0,30 which means you're likely paginating....
If the result you want is a search for only one establishment, and the first image it comes to would work fine .. just use "limit 1" and you'll only get one result.
I took the time to redo the whole model of the database ... and now it works. There was no solution for a system as flawed
Related
Consider the following query:
SELECT p . *
FROM multiple_picks p
WHERE p.event_id = '3303'
AND p.pick = 'Highlanders'
AND ABS( p.score - '8' ) = (
SELECT MIN( ABS( p2.score -1 ) )
FROM multiple_picks p2
WHERE p2.pick = p.pick
AND p2.event_id = p.event_id )
The query above should return the member(s) who selected the CORRECT TEAM AND the MEMBER(S) who was CLOSEST to Selecting the score. If it is a tie - more than one member should be returned!
When I run the above query on the following table, named multiple_picks:
I get the following result back:
MY PROBLEM
The result returned is incorrect as you can clearly see!
Since the winning team was the Highlanders with a score by 8 (as seen in the query)
The correct result which should have been returned is row2 (the member who picked the Highlanders by 10!)
Any advice as to why I am getting this incorrect result, or what I am doing wrong will be greatly appreciated! Been stuck on this for days now!
SQL FIDDLE
The problem here is that p.score - (const) is compared to p2.score - 1 which is not consistent.
Here's the idea (If I understood the question correctly).
Assume you have variables (or something like that):
T = 'Highlanders'
S = 8
This means you need to find the entry which has p.pick=T and for which the p.score is the closest to S.
Here's what you do (replaced the hardcoded values with the variables above to not distract from the essence of the question) :
SELECT p . *
FROM multiple_picks p
WHERE p.event_id = '3303'
AND p.pick = T
AND ABS(p.score - S) = (
SELECT MIN(ABS(p2.score - S))
FROM multiple_picks p2
WHERE p2.pick = p.pick
AND p2.event_id = p.event_id
)
Updated SQL Fiddle
So here is the issue. I'm trying to write a new fillrate report because the one built in is not good enough... I'm trying to run a single select statement to return both, a count of how many times an item was ordered for a specific month, and then also a count of how many times it was invoiced/shipped in full.
This code is obviously wrong, I also currently have it restricted to only look at AUG of 2015, but that is just to simplify results during testing.
I can't figure out how to do the 2nd count... This is what I was trying (brain stuck on old for each loop logic):
select inv_mast.item_id,
inv_mast.item_desc,
"YEAR" = year(oe_line.required_date),
"MONTH" = month(oe_line.required_date),
"ORDERS" = count(1),
"HITS" = (
select count(1)
from invoice_line
where invoice_line.order_no = oe_line.order_no
and invoice_line.oe_line_number = oe_line.line_no
and invoice_line.qty_shipped = oe_line.qty_ordered
)
from oe_line,
inv_mast,
inv_loc
where inv_mast.inv_mast_uid = oe_line.inv_mast_uid
and inv_mast.delete_flag = 'N'
and inv_mast.inv_mast_uid = inv_loc.inv_mast_uid
and inv_loc.location_id = '101'
and year(oe_line.required_date) = '2015'
and month(oe_line.required_date) = '8'
group by inv_mast.item_id,
inv_mast.item_desc,
year(oe_line.required_date),
month(oe_line.required_date)
order by inv_mast.item_id
To me it would seem like you could rewrite the query to use a left join on the invoice_line table instead. Without any proper test data I can't guarantee it is correct, but I think it should be.
Besides the left join I also changed to explicit joins and moved the aliases as I don't think MySQL supports the alias = column syntax.
select inv_mast.item_id,
inv_mast.item_desc,
year(o.required_date) as "YEAR",
month(o.required_date) as "MONTH",
count(1) as "ORDERS",
count(invoice_line.order_no) as "HITS"
from oe_line o
join inv_mast on inv_mast.inv_mast_uid = o.inv_mast_uid
join inv_loc on inv_mast.inv_mast_uid = inv_loc.inv_mast_uid
left join invoice_line on invoice_line.order_no = o.order_no
and invoice_line.oe_line_number = o.line_no
and invoice_line.qty_shipped = o.qty_ordered
where inv_mast.delete_flag = 'N'
and inv_loc.location_id = '101'
and year(o.required_date) = '2015'
and month(o.required_date) = '8'
group by inv_mast.item_id,
inv_mast.item_desc,
year(o.required_date),
month(o.required_date)
order by inv_mast.item_id;
I have kind of a edge-case while working on a WordPress project. I am using Advanced Custom Fields to store metadata about posts, which is stored in a "postmeta" table (whereas posts are stored in a "post" table; here prefixed by "otca_").
The posts here are events, which feature a mandatory evt_date and may feature a evt_date_fin ("fin" stands for "end" in French, sorry about the lame naming convention).
My goal is to select a range of events, keeping only those featuring a evt_date set in the future OR a evt_date_fin set in the future (in the latter case, no matter the evt_date), while ordering by evt_date, so as to display them in a paginated, sorted way.
This is what I came up with:
$today = date('Ymd');
$perPage = 12;
$offset = $perPage * ($paged-1); // $paged equals 1, 2, … n (the current page)
$querystr = "
SELECT SQL_CALC_FOUND_ROWS * FROM otca_posts AS post
INNER JOIN otca_postmeta AS meta ON (post.ID = meta.post_id)
WHERE post.post_type = 'agenda'
AND post.post_status = 'publish'
AND (
(meta.meta_key = 'evt_date' AND CAST(meta.meta_value AS CHAR) >= '". $today ."')
OR (meta.meta_key = 'evt_date_fin' AND CAST(meta.meta_value AS CHAR) >= '". $today ."')
)
ORDER BY (meta.meta_key = 'evt_date' AND CAST(meta.meta_value AS CHAR)) ASC
LIMIT ". $perPage ." OFFSET ". $offset;
$evts = $wpdb->get_results($querystr);
$total = $wpdb->get_var('SELECT FOUND_ROWS()');
// then looping over the $evts and using $perPage / $total to build the pagination links
So, for a given post stored in otca_post, there are several records in the otca_postmeta table referencing this post (using post_id), which differ by their meta_key / meta_value pairs (one pair for evt_date, another for evt_date_fin). I use them to filter out posts in the query, which works fine.
The ORDER BY clause does not actually works though.
I would like to know how I could make it so that the posts are ordered by evt_date, a piece of information stored in otca_postmeta; evt_date is not a row, but rather a value stored in the row "meta_key", which means I need to perform a sub-selection somehow in order to… order.
Thank you.
I do this fairly often in my WordPress plugins - you need to join the posts table to the postmeta table for each of the meta_key's that you care about, in this case "evt_date" and "evt_date_fin". Once you have joined them you can use them in the WHERE clause to compare to DATE(CURRENT_TIMESTAMP). Using the MySql function STR_TO_DATE() will convert the strings stored in the meta_value to a DATE datatype - this example assumes you use a Y-m-d format.
$perPage = 12;
$offset = $perPage * ($paged-1);
// create the SQL query by joining posts to postmeta
$querystr = <<<SQL
SELECT SQL_CALC_FOUND_ROWS
p.*,
STR_TO_DATE(s.meta_value, '%Y-%m-%d') AS start_date,
STR_TO_DATE(e.meta_value, '%Y-%m-%d') AS end_date
FROM otca_posts AS p
-- join postmeta for the start
JOIN otca_postmeta AS s ON p.ID = s.post_id AND s.meta_key = 'evt_date'
-- join postmeta for the end
JOIN otca_postmeta AS e ON p.ID = e.post_id AND e.meta_key = 'evt_date_fin'
WHERE
-- is the start greater than or equal to today?
STR_TO_DATE(s.meta_value, '%Y-%m-%d') >= DATE(CURRENT_TIMESTAMP) OR
-- or is the end greater than or equal to today?
STR_TO_DATE(e.meta_value, '%Y-%m-%d') >= DATE(CURRENT_TIMESTAMP)
-- order the results by the start, then the end, then the post title
ORDER BY start_date, end_date, p.post_title
-- paginate the results
LIMIT $perPage OFFSET $offset
SQL;
// get posts that match
$evts = $wpdb->get_results( $querystr );
// get the total number of results
$total = $wpdb->get_var( 'SELECT FOUND_ROWS()' );
I have this mysql query:
SELECT
freeAnswers.*,
(SELECT `districtCode`
FROM `geodatas`
WHERE `zipCode` = clients.zipCode
GROUP BY `zipCode`
LIMIT 0, 1) as districtCode,
clients.zipCode,
clients.gender,
clients.startAge,
clients.endAge,
clients.mail,
clients.facebook,
surveys.customerId,
surveys.activityId,
surveys.name as surveyName,
customers.companyName,
activities.name as activityName
FROM freeAnswers,
clients,
surveys,
customers,
activities
WHERE freeAnswers.surveyId = surveys.id
AND surveys.customerId = customers.id
AND activities.id = surveys.activityId
AND clients.id = freeAnswers.clientId
AND customers.id = 1
ORDER BY activityName asc
LIMIT 0, 10
the query is correct on my mysql server but when I try to use it in Zend Framework 1.11 model
I get this error: Mysqli prepare error: Operand should contain 1 column(s)
Please, could anyone help me to make it run well?
Best Regards,
Elaidon
Here is some code that should work. Zend_Db_Select doesn't really provide a way to select from multiple tables in the FROM clause without using a JOIN so this feels a bit hackish to me in regards to one small part of the query. Your best bet will probably be to rewrite the query using JOINs where appropriate.
$subselect = $db->select()
->from('geodatas', 'districtCode')
->where('zipCode = clients.zipCode')
->group('zipCode')
->limit(1, 0);
$from = $db->quoteIdentifier('freeAnswers') . ', ' .
$db->quoteIdentifier('clients') . ', ' .
$db->quoteIdentifier('surveys') . ', ' .
$db->quoteIdentifier('customers') . ', ' .
$db->quoteIdentifier('activities');
$select = $db->select()
->from(array('activities' => new Zend_Db_Expr($from)),
array('freeanswers.*',
'districtCode' =>
new Zend_Db_Expr('(' . $subselect . ')'),
'clients.zipCode', 'clients.gender', 'clients.startAge',
'clients.endAge', 'clients.mail', 'clients.facebook',
'clients.customerId', 'clients.activityId',
'surveyName' => 'surveys.name', 'customers.companyName',
'activityName' => 'activities.name'))
->where('freeAnswers.surveyId = surveys.id')
->where('surveys.customerId = customers.id')
->where('activities.id = surveys.activityId')
->where('clients.id = freeAnswers.clientId')
->where('customers.id = ?', 1)
->order('activityName ASC')
->limit(10, 0);
The only reason I say it is hackish is because of the line:
->from(array('activities' => new Zend_Db_Expr($from)),
Since from() really only works with one table, I create a Zend_Db_Expr and specify the correlation as the last table name in the expression. If you don't pass a Zend_Db_Expr, it will either quote your comma separated table name incorrectly, or if you pass an array of table names, it just uses the first. When you pass a Zend_Db_Expr with no name, it defaults to use AS t which also doesn't work in your case. That is why I put it as is.
That returns the exact SQL you provided except for the last thing mentioned. Here is actually what it returns:
SELECT
`freeanswers`.*,
(SELECT `geodatas`.`districtCode`
FROM `geodatas`
WHERE (zipCode = clients.zipCode)
GROUP BY `zipCode`
LIMIT 1) AS `districtCode`,
`clients`.`zipCode`,
`clients`.`gender`,
`clients`.`startAge`,
`clients`.`endAge`,
`clients`.`mail`,
`clients`.`facebook`,
`clients`.`customerId`,
`clients`.`activityId`,
`surveys`.`name` AS `surveyName`,
`customers`.`companyName`,
`activities`.`name` AS `activityName`
FROM `freeAnswers`,
`clients`,
`surveys`,
`customers`,
`activities` AS `activities`
WHERE (freeAnswers.surveyId = surveys.id)
AND (surveys.customerId = customers.id)
AND (activities.id = surveys.activityId)
AND (clients.id = freeAnswers.clientId)
AND (customers.id = 1)
ORDER BY `activityName` ASC
LIMIT 10
So that will work but eventually you will want to rewrite it using JOIN instead of specifying most of the WHERE clauses.
When dealing with subqueries and Zend_Db_Select, I find it easy to write each subquery as their own queries before writing the final query, and just insert the subqueries where they need to go and Zend_Db handles the rest.
Hope that helps.
I have a contact table I wish to query when a certain condition exists. I tried the query below but am getting a syntax error.
SELECT *
FROM contact_details
WHERE contactDeleted` =0
AND IF ( contactVisibility = "private"
, SELECT * FROM contact_details
WHERE contactUserId = 1
, IF( contactVisibility = "group"
, SELECT * FROM contact_details
WHERE contactGroup = 3
)
)
If I'm understanding your question correctly (which is difficult with the lack of info you've provided. Sample datasets and expected outcomes are typically helpful), then I don't believe you need IFs at all for what you want. The following will return contacts that are not deleted and who either have (visibility = "private" and userId = 1) OR (visibility = "group" and group = 3)
SELECT *
FROM contact_details
WHERE contactDeleted = 0
AND (
(contactVisibility = "public")
OR
(contactVisibility = "private" AND contactUserId = 1)
OR
(contactVisibility = "group" AND contactGroup = 3)
)
I am assuming you want to use the IF() function and not the statement which is for stored functions..
Refer to this link for more information on that.
Notice that you have put 2 select statements in there, where the custom return values are supposed to be. So you are returning a SELECT *... now notice that in your upper level sql statement you have an AND.. so you basically writing AND SELECT *.. which will give you the syntax error.
Try using .. AND x IN (SELECT *) .. to find if x is in the returned values.
Let me also list this link to make use of an existing and well written answer which may also applicable to your question.