MySQL Combine Query and ignore duplicates - php

I have two tables images2 and image_data
So the goal is to have 1 table for ALL the image uploads (images2) and then the image_data is to assign that image with different options.
So for example, here is some sample data:
So image_id 10 has more than one row, because this image is associated with both TN and CQ. And they could also be associated with more than one language and slide, so I would add on to the second row and change slide_id or language_id .. if wanted to add more, than I would add a new row for it.
The goal is to have a list of all the images, and then when you click on it it pops up and you can edit the options and change it straight from there.
I need help writing a query. The one I have right now:
SELECT images2.id, images2.filename, image_data.slide_id, image_data.language_id,
image_data.type FROM images2
LEFT JOIN image_data ON images2.id = image_data.image_id
A couple things wrong with that query.. It is showing the duplicates, because image_id 10 has two rows.
But I need that second row of image #10 because I need to see that it is also associated with CQ so I can check the checkbox when it pops up.
So I need to create a query to show ALL the unique images no duplicates, with all of the options it has.
I'm not sure the best way to do this.. do I need to re-do the way my database tables are? Any help is appreciated.
Thanks.

What you could do is use GROUP_CONCAT() to turn values in multiple rows into a single concatenated string. The following retrieves the ids of slides and languages as well as their names to better facilitate your form.
SELECT
a.id,
a.filename,
GROUP_CONCAT(CONCAT(b.slide_id, '::', c.slide_name)) AS slides,
GROUP_CONCAT(CONCAT(b.language_id, '::', d.language_name)) AS languages,
GROUP_CONCAT(b.type) AS types,
FROM
images a
LEFT JOIN
image_data b ON a.id = b.image_id
LEFT JOIN
slides c ON b.slide_id = c.id
LEFT JOIN
languages d ON c.language_id = d.id
GROUP BY
a.id
Your result set for image 10 will look something like:
id | image_filename | slides | languages | types
-------------------------------------------------------------------------
10 | p170sfhe... | 5::slide5 | 1::language1 | TN,CQ
In php, just explode() the strings based on the delimiters.

you could use GROUP_CONCAT to get a csv of the fields:
SELECT
images2.id,
images2.filename,
GROUP_CONCAT(DISTINCT image_data.slide_id) AS slides,
GROUP_CONCAT(DISTINCT image_data.language_id) AS langs,
GROUP_CONCAT(DISTINCT image_data.type) AS types
FROM images2
LEFT JOIN image_data ON (images2.id = image_data.image_id)
GROUP BY images2.id

Related

MySQL join query duplicates users in output

I have the following tables
ea_users
id
first_name
last_name
email
password
id_roles
ea_user_cfields
id
c_id = custom field ID
u_id = user ID
data
ea_customfields
id
name = name of custom field
description
I want to get all users which have a certain role, but I also want to retrieve all the custom fields per user. This is for the backend of my software where all the ea_users and custom fields should be shown.
I tried the following, but for each custom field, it duplicates the same user
$this->db->join('(SELECT GROUP_CONCAT(data) AS custom_data, id AS dataid, u_id, c_id
FROM ea_user_cfields userc
GROUP BY id) AS tt', 'tt.u_id = ea.id','left');
$this->db->join('(SELECT GROUP_CONCAT(name) AS custom_name, id AS customid
FROM ea_customfields AS cf
GROUP BY id) AS te', 'tt.c_id = te.customid','left');
$this->db->where('id_roles', $customers_role_id);
return $this->db->get('ea_users ea')->result_array();
the problem that u did not understand properly how join works.
its ok, that u have duplicates in select when u have relation one to many.
in few words your case: engine tries to fetch data from table "A" (ea_users) then JOIN according to the conditions another table "B" (ea_customfields). If u have one to many relation between tables (it means that one record from table "A" (lets say that we have in this table A1 record) can contain few related rows in table "B", lets call them as B1.1, B1.2 and B1.3 and B1.4), in this case it will join this records and put join result in memory. So in memory u would see something like
| FromTable A | FromTableB |
| A1 | B1.1 |
| A1 | B1.2 |
| A1 | B1.3 |
| A1 | B1.4 |
if u have 10 records in table "B", which related to the table "A" it would put 10 times in memory copy of data from table "A" during fetching. And then will render it to u.
depending on join type rows, with missing related records, can be skipped at all (INNER JOIN), or can be filled up with NULLs (LEFT JOIN or RIGHT JOIN), etc.
When u think about JOINs, try to imagine yourself, when u try to join on the paper few big tables. U would always need to mark somehow which data come from which table in order to be able to operate with it later, so its quite logically to write row "A1" from table "A" as many times as u need to fill up empty spaces when u find appropriate record in table "B". Otherwise u would have on your paper something like:
| FromTable A | FromTableB |
| A1 | B1.1 |
| | B1.2 |
| | B1.3 |
| | B1.4 |
Yes, its looks ok even when column "FromTable A" contains empty data, when u have 5-10 records and u can easily operate with it (for example u can sort it in your head - u just need to imagine what should be instead of empty space, but for it, u need to remember all the time order how did u wrote the data on the paper). But lets assume that u have 100-1000 records. if u still can sort it easily, lets make things more complicated and tell, that values in table "A" can be empty, etc, etc.. Thats why for mysql engine simpler to repeat many times data from table..
Basically, I always stick to examples when u try to imagine how would u join huge tables on paper or will try to select something from this tables and then make sorting there or something, how would u look through the tables, etc.
GROUP_CONCAT, grouping
Then, next mistake, u did not understand how GROUP_CONCAT works:
The thing is that mysqlEngine fetch on the first step structure into memory using all where conditions, evaluating subqueries + appends all joins. When structure is loaded, it tried to perform GROUPing. It means that it will select from temporary table all rows related to the "A1". Then will try to apply aggregation function to selected data. GROUP_CONCAT function means that we want to apply concatenation on selected group, thus we would see something like "B1.1, B1.2, B1.3, B1.4". Its in few words, but I hope it will help a little to understand it.
I googled table structure so u can write some queries there.
http://www.mysqltutorial.org/tryit/query/mysql-left-join/#1
and here is example how GROUP_CONCAT works, try to execute there query:
SELECT
c.customerNumber, c.customerName, GROUP_CONCAT(orderNumber) AS allOrders
FROM customers c
LEFT JOIN orders o ON (c.customerNumber = o.customerNumber)
GROUP BY 1,2
;
can compare with results with previous one.
power of GROUP in aggregation functions which u can use with it. For example, u can use "COUNT()", "MAX()", "GROUP_CONCAT()" or many many others.
or example of fetching of count (try to execute it):
SELECT c.customerName, count(*) AS ordersCount
FROM customers AS c
LEFT JOIN orders AS o ON (c.customerNumber = o.customerNumber)
GROUP BY 1
;
so my opinion:
simpler and better to solve this issue on client side or on backend, after fetching. because in term of mysql engine response with duplication in column is absolutely correct. BUT of course, u can also solve it using grouping with concatenations for example. but I have a feeling that for your task its overcomplicating of logic
PS.
"GROUP BY 1" - means that I want to group using column 1, so after selecting data into memory mySql will try to group all data using first column, better not to use this format of writing on prod. Its the same as "GROUP BY c.customerNumber".
PPS. Also I read comments like "use DISTINCT", etc.
To use DISTINCT or order functions, u need to understand how does it work, because of incorrect usage it can remove some data from your selection, (same as GROUP or INNER JOINS, etc). On the first look, you code might work fine, but it can cause bugs in logic, which is the most complicated to find out later.
Moreover DISTINCT will not help u, when u have one-to-many relation(in your particular case). U can try to execute queries:
SELECT
c.customerName, orderNumber AS nr
FROM customers c
INNER JOIN orders o ON (c.customerNumber = o.customerNumber)
WHERE c.customerName='Alpha Cognac'
;
SELECT
DISTINCT(c.customerName), orderNumber AS nr
FROM customers c
INNER JOIN orders o ON (c.customerNumber = o.customerNumber)
WHERE c.customerName='Alpha Cognac'
;
the result should be the same. Duplication in customer name column and orders numbers.
and example how to loose data with incorrect query ;):
SELECT
c.customerName, orderNumber AS nr
FROM customers c
INNER JOIN orders o ON (c.customerNumber = o.customerNumber)
WHERE c.customerName='Alpha Cognac'
GROUP BY 1
;

Getting Data from multiple tables SQLite

I have an android application that has 2 tables, one table stores posts and the other table stores images of the posts incase it has an image, changes are not all posts will have images, if a post has an image its primary key will be stored in the foreign Key table, when loading the posts I have to get all posts wether with image or not then check in the image table to see which posts have images and add the images to the posts below is a graphical overview of my tables
Post Table
`|post_id |post | post_description|
|--------|-----|-----------------|
| | | |`
Image Table
`|img_id |fk_postID | imagePath |
|--------|----------|-----------------|
| | | |`
I could have used a join like
Query = "SELECT post_id, post, post_description, imagePath FROM PostTable,
ImageTable, Where PostTable.post_id = ImageTable.fk_postID;
but this query is only returning posts with images and forgeting about posts without images, How can I get all posts be it with image or not? Thanks in advance.
ok, you asked, so give this a whirl, see if you like the output
SELECT pt.post_id, pt.post, pt.post_description, im.imagePath
FROM PostTable pt
left join ImageTable im
on im.fk_postID=pt.post_id
It will bring along for the ride the right table (ImageTable) of those posts that don't have images.
Uses table aliases (pt and im). That helps to be explicit which table the columns come from on the first line in case there are common column names in both, plus a little less typing.
Untested
reference Mysql Left Joins
Try using Left join and that will result all entries from left table and matched entries from right table.
SELECT posttable.postid,posttable.post,posttable.postdescription, imagetable.imageid,imagetable.fkpostid,imagetable.imagepath
FROM posttable
LEFT JOIN imagetable
ON posttable.postid=imagetable.fkpostid
ORDER BY posttable.postid;
Code should look like that.
http://www.w3schools.com/sql/sql_join_left.asp

Proper way to join tables in mysql to have a single result

So I have a mysql database with three tables that has three table I am trying to retrieve rows of data from content based on a condition on data phpro_tag_types
The structures of the tables is like so
phpro_tag_types
tag_type_id | tag_type_name
<pk>
phpro_tag_targets
tag_target_id | tag_id | sub_tag_id | tag_target_name | tag_type_id
<pk> | <FK> | <FK> | | <FK>
content
content_id | tag_target_id | bunch of other things|
<pk> | <fk> |
The relationships between the table is like so
content.tag_target_id : phpro_tag_targets.tag_target_id
1 : m //each tag_target_id is in content once
//and phpro_tag_targets many times
phpro_tag_targets.tag_type_id : phpro_tag_types.tag_type_id
M: 1 // there is many occurrences of tag_type_id
//in phpro_tag_targets and one occurrence in of tag_type_id in phpro_tag_type
(I hope I have explained this thoroughly enough using the correct terms if not I apologize, clearly I am still kind of green with this)
Now I have a SQL query that looks like this
SELECT *
FROM phpro_tag_types types
INNER JOIN phpro_tag_targets targets ON types.tag_type_id=targets.tag_type_id
INNER JOIN content c ON targets.tag_target_id = c.tag_target_id
WHERE types.tag_type_id=14
ORDER BY update_time DESC
Now this query works however not exactly quite as I intended. The problem is the resulting array that is returned has multiple instance of the same piece of content ie. a single content_id (I believe because the same tag_target_id exists in phpro_tag_targets multiple times) however I would only like the results array to only contain unique content_id's as this is the data I am actually outputting to users.
As a side note putting Distinct into the query also doesn't seem to work as there is no way to only make sure content is DISTINCT (at least I could find)
Any help with this would be greatly appreciated as I am kind of lost on how to achieve this
"I am trying to retrieve rows of data from content based on a condition on data phpro_tag_types"
Assuming you are trying to fetch fields of content. What about following IN() sub-query
SELECT *
FROM content c
WHERE tag_target_id IN (
SELECT DISTINCT tag_target_id
FROM phpro_tag_types types
INNER JOIN phpro_tag_targets targets ON types.tag_type_id=targets.tag_type_id
WHERE types.tag_type_id=14
)
ORDER BY update_time DESC;
BTW,
"As a side note putting Distinct into the query also doesn't seem to work as there is no way to only make sure content is DISTINCT (at least I could find)"
DISTINCT c.* does not make sense?
SELECT DISTINCT c.*
FROM phpro_tag_types types
INNER JOIN phpro_tag_targets targets ON types.tag_type_id=targets.tag_type_id
INNER JOIN content c ON targets.tag_target_id = c.tag_target_id
WHERE types.tag_type_id=14
ORDER BY update_time DESC;
There's a general trick to do a left outer join on the duplicating table in such a way that only one entry has null values and then limit the query to that row:
SELECT *
FROM phpro_tag_types types
INNER JOIN phpro_tag_targets targets ON types.tag_type_id=targets.tag_type_id
INNER JOIN content c ON targets.tag_target_id = c.tag_target_id
LEFT OUTER JOIN phpro_tag_targets t2 on targets.tag_target_id=t2.tag_target_id
AND t2.tag_id < targets.tag_id
WHERE types.tag_type_id=1
AND t2.tag_target_id IS NULL;
This seems a bit crazy but does work and in my experience is performant unless you're dealing with a 'very large' data set (whatever that means).
I'm not 100% sure what the semantics here are, but this query is assuming that you don't care what 'tag_id' you get, you just want to get any tag that matches the 'type_id', which looks to me to match your intent.
NOTE: this will cause duplicate column names in the '*' select, so you need to limit to types.*, targets.*, content.* or spell out the column names.

Get a picture for each album

I have two table for gallery system :
gallery_cat(
gallery_cat_id PK,
gallery_cat_name
)
gallery(
gallery_id PK,
gallery_cat_id FK,
gallery_name,
gallery_file_name,
gallery_date
)
I need to write a SQL query that return one picture from gallery table for each album, the purpose of this that I need to list the albums with one picture for each.
gallery_name | gallery_cat_name| gallery_file_name
-------------+-----------------+------------------
pic1 | Album1 | pic1.jpg
This should do the trick:
SELECT g2.gallery_name, gc2.gallery_cat_name, g2.gallery_file_name
FROM gallery g2
INNER JOIN gallery_cat gc2 ON (g2.gallery_cat_id = gc2.gallery_cat_id)
WHERE g2.gallery_id IN (
SELECT g.gallery_id
FROM gallery g
GROUP BY g.gallery_cat_id)
Explanation:
At the end is a sub-select
IN (
SELECT g.gallery_id
FROM gallery g
GROUP BY g.gallery_cat_id) <<-- select 1 random g.id per gallery_cat.
Here I select all g.id, but because of the group by clause it will reduce the results to 1 row per grouped by item. I.e. 1 row (chosen more or less at random) per g.gallery_cat_id.
Next I do a normal select with a join:
SELECT g2.gallery_name, gc2.gallery_cat_name, g2.gallery_file_name
FROM gallery g2
INNER JOIN gallery_cat gc2 ON (g2.gallery_cat_id = gc2.gallery_cat_id)
WHERE g2.gallery_id IN (
Because I refer to the same table twice in the same query you have to use an alias(*).
I select all names and all catnames and all filenames.
However in the where clause I filter these so that only rows from the sub-select are shown.
I have to do it this way, because the group by mixes rows into one messed up ow, if I select from that directly I will get values from different rows mixed together, not a good thing.
By first selecting the id's I want and then matching full rows to those id I prevent this from happening.
*(in this case with this kind of subselect that's not really 100% true, but trust me on the point that it's always a good idea to alias your tables)
This attempts to select the most recent gallery_date for each category ID and join against gallery_cat
SELECT
c.gallery_cat_id,
c.gallery_cat_name,
i.lastimg
FROM
gallery_cat c
LEFT JOIN (
SELECT gallery_cat_id, gallery_filename AS lastimg, MAX(gallery_date)
FROM gallery
GROUP BY gallery_cat_id, gallery_filename
) i ON c.gallery_cat_id = i.gallery_cat_id
You can use SQL JOINS to do this, otherwise you would have to loop out all the albums and pick one random picture from each which would be less efficient.

How to use join for more than two table?

I have three tables.They are
tb_albums---->id,title, description
tb_photos---->id,album_id, photo
tb_tags---->id,album_id, tag
Here i want to get the albums details and it photos & it tags through tb_albums.id.
How to use join query here?
Normally, you can use as many tables in JOIN, as you'd like. Just add another JOIN statement. You can refer to k102's answer for the correct syntax (which doesn't produce correct result though).
But in this particular case you don't want to use simple JOIN on all tables, unless you have only one photo per album and only one tag per album. If you have more than one photo per and more than one tag per album, JOIN both tables on album_id in single query will produce Cartesian product of both, in other words all possible combinations of tags and photos from each albums. For N photos and M tags that's N * M results, instead of N + M.
Also, there is no point of joining with tb_albums, as you do not need to repeat information about each album for each photo and each tag.
Proper approach would be to have 3 separate simple SELECTs from each table and combining their result on application level.
If for some awkward reason you'd need to do that with one query, you can do something like:
SELECT * FROM tb_albums as A JOIN
(SELECT 'photo', id, photo as value FROM tb_photos
UNION ALL
SELECT 'tag', id, tag as value FROM tb_tags) as B ON B.album_id = A.id
Note, this is way less optimal than separate SELECTs, you should only do this if you have no other choice.
select * from tb_albums a
join tb_photos p on a.id = p.album_id
join tb_tags t on a.id = t.album_id

Categories