Convert multiple select query to Doctrine DQL - php

Tables:
items with field id
id
--
1
2
3
item_photos with fields id, item_id, photo,status. Status is 0 or 1.
id | item_id | photo | status
-----------------------------
1 1 a.jpg 1
2 1 b.jpg 1
3 1 c.jpg 2
4 2 d.jpg 1
item_photos item_id is a foreign key on items.id
Entities:
Item entity has OneToMany on ItemPhoto
class Item {
/**
* #ORM\OneToMany(targetEntity="ItemPhoto", mappedBy="item")
*/
private $itemPhotos;
Problem:
I want to return all records from items, together with either one photo with status=2 or any other first photo for the same item_id in the item_photos table, or null if no photos found, in a single query.
Note: not all items have photos in item_photos with status=2.
In a Repository class I have this so far, it works but not what I want:
//Solution can be using query builder, or create query, doesn't matter
//
$em = $this->getEntityManager();
$dql = "SELECT i,
(SELECT p.photo FROM AppBundle:ItemPhoto p WHERE p.status='2' AND p.item=i.id) as photo1,
FROM AppBundle:Item i";
$query = $em->createQuery($dql);
When I add another select into this query (SELECT p.photo FROM AppBundle:ItemPhoto p WHERE p.item=i.id LIMIT 0,1) as photo2, it gives me error. I could write it with SQL using IFNULL() but then DQL doesn't support IFNULL out of the box.

Related

How can i use sql count in these multiple tables?

I am still a php/mysql newbie and I am working on mysql table relationship concept and i am having an issue with using mysql count in multiple table. Here is my db structure.
**product table**
id product_name product_img groupeid
1 Sneaker Mark sneaker_adi.png 1
2 bag Eric bageric.png 2
3 Sneaker Etoi sneakeretoi.jpg 1
**groupe table**
group_id group_name
1 men
2 women
**category table**
catid catname
1 sneaker-shoes
2 bag-woman
**productcategory table**
prod_id cat_ID
1 1
2 2
3 1
What i want to do is to determine the number of sneaker-shoes using mysql.
We can see that the number of sneaker-shoes in the db is 2.
But how can i use **count()** in these multiple tables.
I tried like this;
$sql = "SELECT COUNT(*) product.id,product_name,catname FROM product INNER JOIN productcategory ON product.id = prod_id INNER JOIN category ON catid = cat_ID WHERE catname='sneaker-shoes'";
i got error like:
Fatal error: Call to a member function execute() on a non-object in C:\wamp\www\kbashopping\Homme\index.php on line 32
Hope i exposed the issue clearly, any help and assistance will be appreciate
Thanks
If you are looking only for the count, mention only the count phrase in the Select clause.
Change :
SELECT COUNT(*) product.id,product_name,catname FROM
to :
SELECT COUNT(product.id) FROM
SELECT count (pc.cat_ID) FROM productcategory pc inner join category c on c.catid = pc.cat_ID where c.catname = 'sneaker shoes';
This will build a temporary table in mysql that joins category and product category but only including results where the catname is sneaker shoes. Then it selects a column to run the count operation on, and returns the result of count.

Fetch data from mapping table if the condition matches in multiple rows

product_id property_id
1 2
1 5
2 2
3 5
I have a mapping table as above. I want to get only product with id =1 if product_id in (2,5). i.e. I want to fetch data if the table contains both 2,5 not the data if it is with property_id only 2 or 5
select group_concat(distinct product_id) product_ids from table where property_id in (2,5)
UPDATE:
The property_id in can be property_id in(2,5,....). I get output from form as 2,5,.... and so on. Its not just for the single case. I just want the output if the condition in property_id in matches the whole series.
This how it could be done
select
product_id from
table_name
where property_id in (2,5)
group by product_id
having count(*) = 2
All you need to change having count(*) = 2 to the number of items inside IN() , right now its 2 and if you are looking at 3 property id then it will be 3 and so on.
select distinct a.product_id
from table a, table b
where a.product_id = b.product_id
and a.property_id = 2
and b.property_id = 5

SQL select all using JOIN

I'm using this query to collate two sets of results but I now need to use JOIN instead of UNION to get the second part of the data from another table.
However I need quite a lot of fields and can't seem to find a way to maintain the use of SELECT * when using JOIN.
mysql_query("SELECT * FROM table.products WHERE category='$cat' GROUP BY product_id ORDER BY id UNION ALL SELECT * FROM table.products WHERE type='red' GROUP BY product_id ");
Table - products
product_id | title | category | id
0 one home 10
1 two home 11
1 two - a home 12
2 three work 13
Table - product_details
product_id | type | size |
0 blue S
1 blue M
1 red L
Ultimately I need to list every product in the first table for a given category e.g home,
as there is sometimes two entries or more for a single product id, I need to only select one row for each product id value. I also need to join the second table so I can get the size info, however I must be able to get the size info by preferring a type e.g red.
So for this example I would get a list like:
product_id | title | category | type | size
0 one home blue S
1 two home red L
This excludes product_id 2 as it's not in the home category, the first entry for product_id equaling 1 is selected because of the GROUP BY and ORDER BY and the information on size for product_id 1 is L because it is of type red not blue.
Assuming you are using MySQL, you want a join with an aggregation or aggressive filtering. Here is an example using join and aggregation:
select p.product_id, p.title, p.category,
substring_index(group_concat(pd.type order by pd.type = 'red' desc, pd.type), ',', 1) as type,
substring_index(group_concat(pd.size order by pd.type = 'red' desc, pd.type), ',', 1) as size
from products p join
product_details pd
on p.product_id = qpd.product_id
where p.category = 'home'
group by p.product_id;
The expression substring_index(group_concat(. . .)) is choosing one type (and one size) with precedence given to the red type.
Your query can be simplified like below since you are using the same table table.products. Not sure why you need to UNION them.
SELECT * FROM table.products
WHERE category='$cat'
and type='red'
GROUP BY product_id
EDIT:
With your edited post, the query should look like
select p.product_id,p.title,p.category,q.type,q.size
from products p join product_details q
on p.product_id = q.product_id
where p.category = 'home'
and q.type = 'red'

Help with limiting a joined mysql database query

I have written a query which returns all records with some many-to-many joins correctly for the entire set or an individual article using WHERE a.id = ?
SELECT a.id, date_added, title, content, category_id, person_id, organization_id, c.name AS category_name, firstname, lastname, o.name AS organization_name
FROM articles AS a
LEFT OUTER JOIN articles_categories AS ac ON a.id=ac.article_id
LEFT OUTER JOIN categories AS c ON c.id=ac.category_id
LEFT OUTER JOIN articles_people AS ap ON a.id=ap.article_id
LEFT OUTER JOIN people AS p ON p.id=ap.person_id
LEFT OUTER JOIN articles_organizations AS ao ON a.id=ao.article_id
LEFT OUTER JOIN organizations AS o ON o.id=ao.organization_id
ORDER BY date_added
BUT!
I've hit a brick wall trying to work out how to limit the articles to a specific number of IDs, for working with pagination.
I'm ideally trying to use as simple and clear SQL statements as possible because I'm using the codeigniter framework with their active record class.
http://codeigniter.com/user_guide/database/active_record.html
Would really appreciate some help as I don't want to revert to using multiple queries for this as I've tried to reduce it down to a single query for database efficiency.
Have search around and tried some alternatives but nothing seems to work. Many thanks!
For example the results I return are like this
---------------------------------------------------------------------
id title category_id person_id organization_id
---------------------------------------------------------------------
1 test 1 1 1
1 test 2 1 1
1 test 1 2 1
1 test 1 1 2
1 test 5 1 1
1 test 8 1 1
1 test 1 4 1
1 test 1 4 2
1 test 1 1 1
2 test 2 2 1 1
2 test 2 1 2 1
2 test 2 1 1 2
2 test 2 5 1 1
2 test 2 8 1 1
2 test 2 1 4 1
2 test 2 1 4 2
I need the results like this so that I can create sub-arrays in the php like this:
$articles = $query->result_array();
$output = array();
foreach ($articles as $article) {
// set up article details
$article_id = $article['id'];
// add article details
$output[$article_id]['article_id'] = $article_id;
$output[$article_id]['date_added'] = $article['date_added'];
$output[$article_id]['title'] = $article['title'];
$output[$article_id]['content'] = $article['content'];
// set up people details and add people array with details if exists
if (isset($article['person_id'])) {
$person_id = $article['person_id'];
$output[$article_id]['people'][$person_id]['person_id'] = $person_id;
$output[$article_id]['people'][$person_id]['lastname'] = $article['lastname'];
$output[$article_id]['people'][$person_id]['firstname'] = $article['firstname'];
}
// set up organizations details and add organizations array with details if exists
if (isset($article['organization_id'])) {
$organization_id = $article['organization_id'];
$output[$article_id]['organizations'][$organization_id]['organization_id'] = $organization_id;
$output[$article_id]['organizations'][$organization_id]['organization_name'] = $article['organization_name'];
}
// set up categories details and add categories array with details if exists
if (isset($article['category_id'])) {
$category_id = $article['category_id'];
$output[$article_id]['categories'][$category_id]['category_id'] = $category_id;
$output[$article_id]['categories'][$category_id]['category_name'] = $article['category_name'];
}
}
But if I just use LIMIT (with offset etc) 1
the results I get are
---------------------------------------------------------------------
id title category_id person_id organization_id
---------------------------------------------------------------------
1 test 1 1 1
instead of
---------------------------------------------------------------------
id title category_id person_id organization_id
---------------------------------------------------------------------
1 test 1 1 1
1 test 2 1 1
1 test 1 2 1
1 test 1 1 2
1 test 5 1 1
1 test 8 1 1
1 test 1 4 1
1 test 1 4 2
1 test 1 1 1
which is my desired result.
OK, so finally I worked out how it is possible.
Thought i'd include it here in case anyone else has the same problem.
Changing this line
FROM articles AS a
to this
FROM (SELECT * FROM articles LIMIT 5,3) AS a
does what I wanted.
So, why don't you use OFFSET 0,10 and LIMIT *number_of_results* in the SQL Query? (if I understood the question)
Specific number of IDs... WHERE ID IN (2,4,6,8)... ?
Are you using codeigniter's pagination?
http://codeigniter.com/user_guide/libraries/pagination.html
You can easily limit the number of records that are being returned using the MySQL LIMIT clause. This can be achieved like the following with your sample query.
SELECT a.id, date_added, title, content, category_id, person_id, organization_id, c.name AS category_name, firstname, lastname, o.name AS organization_name
FROM articles AS a
LEFT OUTER JOIN articles_categories AS ac ON a.id=ac.article_id LEFT OUTER JOIN categories AS c ON c.id=ac.category_id
LEFT OUTER JOIN articles_people AS ap ON a.id=ap.article_id LEFT OUTER JOIN people AS p ON p.id=ap.person_id
LEFT OUTER JOIN articles_organizations AS ao ON a.id=ao.article_id LEFT OUTER JOIN organizations AS o ON o.id=ao.organization_id
ORDER BY date_added
LIMIT 10
Where 10 is the number of records you wish to display. The MySQL LIMIT clause allows you to specify a limit of the number of records and an initial offset. Like so:
LIMIT <offset>,<limit>
In your case <offset> would be the current page * the number of records on a page. <limit> would be the number of records you would like to display per page.

where id = multiple artists

Any time there is an update within my music community (song comment, artist update, new song added, yadda yadda yadda), a new row is inserted in my "updates" table. The row houses the artist id involved along with other information (what type of change, time and date, etc).
My users have a "favorite artists" section where they can do just that -- mark artists as their favorites. As such, I'd like to create a new feature that shows the user the changes made to their various favorite artists.
How should I be doing this efficiently?
SELECT *
FROM table_updates
WHERE artist_id = 1
OR artist_id = 500
OR artist_id = 60032
Keep in mind, a user could have 43,000 of our artists marked as a favorite.
Thoughts?
This depends on how your database is setup. If I had my way, I'd set it up with a table like so:
Table: user_favourite_artist
user_id | artist_id
---------------------
1 | 2
1 | 8
1 | 13
2 | 2
3 | 6
6 | 20
6 | 1
6 | 3
user_id and artist_id together would be a composite primary key. Each row specifies a user, by id, and an artist they have as a favourite, by id. A query like so:
SELECT artist_id FROM user_favourite_artist WHERE user_id = 1
Would give you the artist_id's 2, 8, and 13. This is a very simple query that will scale to your expectations.
On the reverse, when an artist is updated, you'd run this query:
SELECT user_id FROM user_favourite_artist WHERE artist_id = 2
And you would get the user_id's 1 and 2. This will tell you which users to notify. This query is also simple and will scale.
Maybe you can try this:
SELECT *
FROM table_updates
WHERE artist_id IN(1, 500, 60032)
If you have the marked artists in a secondary table, I would recomend rather using a join.
Something like
SELECT *
FORM table_updates tu INNER JOIN
table_marked_by_user tmbu ON tu.artist_id = tmbu.artist_id
WHERE tmbu.user_id = $user_id
If you're on SQL Server, you can use a nested select statement:
select * from table_updates where artist_id in
(select artist_id from favorites_table where user_id = 10)
If you don't mind doing dirty reads, you can speed it up with (nolock).
select * from table_updates (nolock) where artist_id in
(select artist_id from favorites_table (nolock) where user_id = 10)

Categories