Perform an Inner Join in Zend PHP Framework - php

I want to perform an inner join on two table.
Table A -
item_id
item_title
varX
Table B -
item_id
varY
someVar
This is how I've done this using a RAW SQL query.
$sql = 'SELECT tableA.item_id, tableY.item_title AS Name, 5 * varX + 5 * count(*) AS myScore
FROM tableA
INNER JOIN tableY ON tableA.item_id=tableY.item_id
WHERE someVar=\'8\'
GROUP BY item_id
ORDER BY myScore DESC
LIMIT 10';
$stmt = $this->_db->prepare($sql);
$stmt->execute();
$result = $stmt->fetchAll();
Now I want to do this using a Zend Query.
This is what I've written -
$data = $this->_db->select()
->from(array('tablA'=>'tableA'), array('item_id', 'item_title'), 'myScore'=>'(5*'tableA'.'varX') + 5*count(*)')
->joinInner(array('tablB'=>'tableB'), 'tablA'.'item_id' = 'tablB'.'item_id')
->where('someVar = 8')
->GROUP('item_id')
->order('myScore DESC')
->limit(10);
$dataResult = $this->_db->fetchAll($data);
But I get this error -
syntax error, unexpected '=>' (T_DOUBLE_ARROW), expecting ',' or ')'
in line ->from(array('tablA'=>'tableA'), array('item_id', 'item_title'), 'myScore'=>'(5'tableA'.'varX') + 5*count()')
Not sure what to do do here as I've read the official documentation but still can't figure this out. Any help is appreciated!

The quotes were used in wrong way in your code. Also, tt seems like, you have used the unnecessary third parameter for 'myScore' field. It should be placed in the second parameterTry the following:
...
$data = $this->_db->select()
->from(array('tablA'=>'tableA'), array('item_id', 'item_title', 'myScore'=>'(5 * tableA.varX) + 5*count(*)'))
->joinInner(array('tablB'=>'tableB'), 'tablA.item_id = tablB.item_id')
->where('someVar = 8')
->group('tablA.item_id')
->order('myScore DESC')
->limit(10);

Related

PHP/SQL: How to concatenate/combine columns value into one row

I have this php script called title, where it is supposed to list movie details of those movies with the title matching the inputed substring. The expected output is supposed to be like in the link/picture below. I have trouble with concatenating the genres of each movies since one movie can have many genres. I have tried using the concat(), array_to_string() but still fails.
mkSQL() constructs "safe" SQL query strings by taking a query template
string and filling in printf-like slots in the template with values
supplied in subsequent arguments. The function takes a variable number
of arguments; the first is always a query template string, with the
following arguments corresponding exactly to the slots in the
template. E.g.
$id = 3012345;
$q1 = mkSQL("select * from R where id = %d",$id);
would create the query strings:
$q1: "select * from R where id = 12345"
Below are the codes, any helps and tips will be greatly appreciated, thanks!
This is the Genre Table Schema
CREATE TABLE Genre (
movie_id integer REFERENCES Movie(id),
genre GenreType,
primary key (movie_id,genre));
#!/usr/bin/php
<?php
// include the common PHP code file
require("a2.php");
$db = pg_connect("dbname=mydb");
// Check arguments
if (count($argv) < 2) exit("$usage\n");
// Get the return results
$val = $argv[1];
$q = "select m.title, m.year, m.content_rating, r.imdb_score, array_to_string(array(select g.genre FROM Genre g where g.movie_id = m.id),',')
-- concat(select g.genre FROM Genre g where g.movie_id = m.id
from Movie m JOIN Rating r ON r.movie_id = m.id
where m.title ilike %p
order by m.year, r.imdb_score desc, m.title asc";
$r = pg_query($db, mkSQL($q, $val));
// Iterate through the results and print
$i = 1;
while ($t = pg_fetch_array($r)) {
echo "$i. $t[0] ($t[1], $t[2], $t[3]) [$t[4]]\n";
$i++;
}
?>
The expected output is supposed to be in this format
Change your query like,
SELECT CONCAT(m.title, ' (', m.year, ', ', m.content_rating, ',', r.imdb_score, ') [', (SELECT array_to_string(array_agg(g.genre), ',') FROM Genre g WHERE g.movie_id = m.id), ']') movie_title
FROM Movie m JOIN Rating r ON r.movie_id = m.id
WHERE m.title ilike %p
ORDER BY m.year, r.imdb_score desc, m.title ASC
Here, I have concat all columns into one and given it an alias movie_title. You will get the movie name as per your specified format.
For achieving this, you can use the group_concat function in your mysql script.
This will concatenate your respective column via comma(,).

Mysql query IN clause return results

Good Day! All Fridays,
I have some problem in my sql query. I'm using IN class with subquery like this
SELECT
cm.category_id,
cd.name
FROM
category_master cm,
category_detail cd,
brand_to_categories b2c
WHERE
cm.category_id = b2c.category_id
AND
cd.category_id = cm.category_id
AND
cd.language_id = 1
AND
cm.status <> 2
AND
cm.category_id IN (SELECT DISTINCT sub_dd.categories FROM distribution_master bdm, distribution_detail bdd, subscription_category_to_brand_user sub_dd WHERE bdd.distribution_id = bdm.distribution_id AND bdm.distributor_id = 35 AND bdd.brand_id = 7191 AND sub_dd.sub_d_id = bdd.id)
AND
b2c.brand_id = 7191;
The following is the sub-query which is creating problem for me.
cm.category_id IN (
SELECT DISTINCT
sub_dd.categories
FROM
distribution_master bdm,
distribution_detail bdd,
subscription_category_to_brand_user sub_dd
WHERE
bdd.distribution_id = bdm.distribution_id
AND
bdm.distributor_id = 35
AND
bdd.brand_id = 7191
AND
sub_dd.sub_d_id = bdd.id)
the result of the sub-query is like this.
3913,4517,6059,7137,7138,7139,7140,7141,7144
this result is coming from only single row in the target table because I stored these ids as string in the filed.
Now the problem is this, I can not get results of the all categories. Main query final result only return one category information which category_id is 3913. But if I run this query manually with sub-query values instead of the sub-query then it returns all the categories results.
Manual query with sub-query values is like this
SELECT
cm.category_id,
cd.name
FROM
category_master cm,
category_detail cd,
brand_to_categories b2c
WHERE
cm.category_id = b2c.category_id
AND
cd.category_id = cm.category_id
AND
cd.language_id = 1
AND
cm.status <> 2
AND
cm.category_id IN (3913,4517,6059,7137,7138,7139,7140,7141,7144)
AND
b2c.brand_id = 7191;
Please help me regarding this problem.
Sorry I forget, I'm using Mysql
Assuming you are using MySQL, use FIND_IN_SET:
WHERE
...
FIND_IN_SET(cm.category_id,
(SELECT DISTINCT sub_dd.categories
FROM distribution_master bdm,
distribution_detail bdd,
subscription_category_to_brand_user sub_dd
WHERE bdd.distribution_id = bdm.distribution_id AND
bdm.distributor_id = 35 AND
bdd.brand_id = 7191 AND
sub_dd.sub_d_id = bdd.id)) > 0
If you are using SQL Server, then we have to do a bit more work:
WHERE ',' + (SELECT DISTINCT ...) + ',' LIKE '%,' + cm.category_id + ',%'
General comment: Avoid storing CSV data in your SQL tables. MySQL almost made the problem worse by offering FIND_IN_SET and making it easier to skirt good table design.

SQL Error when trying to JOIN tables

I'm trying to run this query:
$query = $conn->query("SELECT * FROM $table
LEFT JOIN vacatures ON bedrijven.id = vacatures.id
WHERE id = '$id'
AND bedrijfID = '$bedrijf_id'");
But it fails for some reason. I get this error.
Syntax error or access violation: 1066 Not unique table/alias
When I leave the JOIN part, the query is succesful. Why is this happening?
I'm using PHP & PDO to fetch the queries.
Thanks.
EDIT: I wrote the query thanks of the answers given. This is working:
$query = $conn->query("SELECT * FROM bedrijven
LEFT JOIN vacatures v ON bedrijven.id = v.bedrijfID WHERE v.bedrijfID = $bedrijf_id AND v.id = $id");
You need to specify one table or the other in WHERE id = '$id', even though they're equal to each other in this case.
You also need to make sure your LEFT JOIN includes $table:
$query = $conn->query("SELECT * FROM $table
LEFT JOIN vacatures ON $table.id = vacatures.id
WHERE $table.id = '$id'
AND bedrijfID = '$bedrijf_id'");
or:
$query = $conn->query("SELECT * FROM bedrijven
LEFT JOIN vacatures ON bedrijven.id = vacatures.id
WHERE bedrijven.id = '$id'
AND bedrijfID = '$bedrijf_id'");
Your question isn't super clear, but if you're just trying to do a simple join where the id on table 1 = id on table 2, then the below statement would work. If that's what you're attempting to do, then the AND statement is redundant. Hard to know what you're going for without a clearly defined question with clearly defined variables. Also, use prepared statements as shown below rather than inserting variables directly into your statement. And avoid SELECT * whenever possible. Only select what is absolutely necessary.
$query = $conn->prepare("SELECT * FROM bedrijven b
LEFT JOIN vacatures v
ON b.id = v.id
WHERE v.id = :id");
$query->bindValue(':id', $id);
$query->execute();
The '$id' will be treated as string not as variable.and you need to specify the id as table.id if both of the tables have a field called id.
$query = $conn->query("SELECT * FROM $table LEFT JOIN vacatures ON bedrijven.id = vacatures.id WHERE $table.id = $id AND bedrijfID = $bedrijf_id");

How To Optimize PostgreSQL generate_series function

I have a query that uses PostgreSQL generate_series function but when it comes to large amounts of data, the query can be slow. An example of code the generates the query is below:
$yesterday = date('Y-m-d',(strtotime ( '-1 day' ) ));
$query = "
WITH interval_step AS (
SELECT gs::date AS interval_dt, random() AS r
FROM generate_series('$yesterday'::timestamp, '2015-01-01', '1 day') AS gs)
SELECT articles.article_id, article_title, article_excerpt, article_author, article_link, article_default_image, article_date_published, article_bias_avg, article_rating_avg
FROM development.articles JOIN interval_step ON articles.article_date_added::date=interval_step.interval_dt ";
if (isset($this -> registry -> get['category'])) {
$query .= "
JOIN development.feed_articles ON articles.article_id = feed_articles.article_id
JOIN development.rss_feeds ON feed_articles.rss_feed_id = rss_feeds.rss_feed_id
JOIN development.news_categories ON rss_feeds.news_category_id = news_categories.news_category_id
WHERE news_category_name = $1";
$params = array($category_name);
$query_name = 'browse_category';
}
$query .= " ORDER BY interval_step.interval_dt DESC, RANDOM() LIMIT 20;";
This series looks for only content that goes one day back and sorts the results in random order. My question is what are was that generate_series can be optimized to improve performance?
You don't need that generate_series at all. And do not concatenate query strings. Avoid it by making the parameter an empty string (or null) if it is not set:
if (!isset($this -> registry -> get['category']))
$category_name = '';
$query = "
select articles.article_id, article_title, article_excerpt, article_author, article_link, article_default_image, article_date_published, article_bias_avg, article_rating_avg
from
development.articles
inner join
development.feed_articles using (article_id)
inner join
development.rss_feeds using (rss_feed_id)
inner join
development.news_categories using (news_category_id)
where
(news_category_name = $1 or $1 = '')
and articles.article_date_added >= current_date - 1
order by
date_trunc('day', articles.article_date_added) desc,
random()
limit 20;
";
$params = array($category_name);
Passing $yesterday to the query is also not necessary as it can be done entirely in SQL.
If $category_name is empty it will return all categories:
(news_category_name = $1 or $1 = '')
Imho, try removing that random() in your order by statement. It probably has a much larger performance impact than you think. As things are it's probably ordering the entire set by interval_dt desc, random(), and then picking the top 20. Not advisable...
Try fetching e.g. 100 rows ordered by interval_dt desc instead, then shuffle them per the same logic, and pick 20 in your app. Or wrap the entire thing in a subquery limit 100, and re-order accordingly along the same lines.

How do I perform this query without relying on looping in PHP?

I'm brand new with MySQL. It appears like I need to do some sort of loop in a MySQL request, but I think it could be way more efficient with some "INNER JOIN" stuff.
Here is my PHP code:
$query = 'SELECT id FROM membres WHERE pseudo = :pseudo LIMIT 1';
$req = $dtb -> prepare($query);
$req -> execute(array(
'pseudo' => $_COOKIE['pseudo']
));
while($donnes = $req -> fetch()){
$id_pseudo = $donnes['id'];
}
$req -> closeCursor();
$query = 'SELECT id_chanson FROM samples WHERE id_membre = :id_pseudo';
$req = $dtb -> prepare($query);
$req -> execute(array(
'id_pseudo' => $id_pseudo
));
$id_chansons = array();
while($donnes = $req -> fetch()){
$id_chansons[] = $donnes['id_chanson'];
}
$req->closeCursor();
$nSongs= count($id_chansons);
$query = 'SELECT nom, pathName, date FROM chansons WHERE id = :id_chanson';
$req = $dtb -> prepare($query);
for($i=0;$i<$nSongs;$i++){
$req -> execute(array(
'id_chanson' => $id_chansons[$i]
));
while($donnes = $req -> fetch()){
$nomChanson[$i] = $donnes['nom'];
$pathName[$i] = $donnes['pathName'];
$date[$i] = $donnes['date'];
}
}
EDIT: my table names are "chansons" (songs in french) "membres" (users) and "samples"
:)
You're right, it's often much more efficient to run a single query rather than multiple queries. It's faster on the database (fewer round trips), and it makes for much friendlier code.
Here's an example SQL query that will retrieve the rows from chansons:
SELECT c.nom
, c.pathName
, c.date
FROM (SELECT id FROM membres WHERE pseudo = :pseudo ORDER BY id LIMIT 1) m
JOIN samples s
ON s.id_membre = m.id
JOIN chansons c
ON c.id = s.id_chanson
ORDER BY 1
NOTES:
If you need the id values from the other tables returned in the result set, expressions for those can be included in the SELECT list as well. (From your code, I don't see that those id values being used for anything other than a subsequent query... but of course that doesn't mean they aren't referenced elsewhere.
I added an ORDER BY to the query in the inline view, to make it deterministic. If there is more than one row that satisfies the query, absent an ORDER BY clause, it is arbitrary which row will be returned by MySQL, that is, the result returned from a subsequent execution is not guaranteed to be the same as the previous run.)
The query above makes use of an inline view (MySQL calls it a derived table) aliased as m, to get the LIMIT 1 on the number of rows returned from membres. If you don't need that LIMIT, then the query can be simplified by removing that inline view.
The INNER keyword is optional, it has no influence on the optimizer. That is, INNER JOIN is synonymous with JOIN.
select m.id, s.id_chanson, ch.nom, ch.pathName, ch.date
from membres m
inner join samples s on m.id = s.id_membre
inner join chansons ch on s.id_chanson = ch.id
where pseudo = :pseudo

Categories