Advanced mysql queries, fetching from several tables and rows at once. Joins? - php

I'm currently working on a project where I have information stored in several tables that all connect to each other. I believe that the table and column format is logical and the best choice. The problem though, is that I don't have enough knowledge to build queries advanced enough to fetch all the information I need.
The main table is ab_ads, where advertisements are stored. These ads can be assigned several formats (ie. 250x360, 980x120 etc), and you can also select the region where they should be showing (ie. Skåne, Stockholm, Kalmar, Dalarna, Jämtland etc).
This is how I store my data. I'm not showing all tables but I hope this is sufficient.
Advertisements column (ab_ads): (There are more columns but they are not relevant)
ID orgnum company_name title content link
1 556664-7524 Company Inc Lorem ipsum Lorem ipsum URL
Advertisement states (ab_ads_states):
ID adID stateID
1 1 2 // Skåne
2 1 5 // Kalmar
3 1 8 // Stockholm
4 1 10 // Värmland
5 2 2 // Skåne
6 2 5 // Kalmar
7 3 8 // Stockholm
8 4 10 // Värmland
Advertisement formats (ab_ads_formats)
ID adID formatID
1 1 1 // 250x360
2 1 2 // 980x120
3 2 1 // 250x360
4 3 2 // 980x120
Formats table (ab_formats)
ID name width height
1 Format 1 250 360
2 Format 2 980 120
So, I have two flash banners both are supposed to call a PHP-script which in turn is supposed to deliver an XML-file back with all the results.
I know how to select data from different tables, but I've never worked with selecting multiple rows from another table and merging them into one, which I suppose is that I need to do here. I'm very thankful for any help I can get.
The flash banners will send two parameters to the PHP file, stateID and formatID. Which means I have to SELECT ad WHERE state = param AND format = format. But since I store multiple entries for the ad states I don't know how to do it.
EDIT:
I would also like to fetch the format names in the query and get them in the following format: "Format 1,Format 2" in a column named "formats". I guess this would require some kind of join?
Thanks in advance!

I think this will work:
select ab.name as formats, aa.* from ab_ads as aa
inner join ab_ads_states as aas on aa.id = aas.adid and aas.stateId = stateIdParam
inner join ab_ads_formats as aaf on aa.id = aaf.adid and aaf.formatId = formatIdParam
inner join ab_formats as ab on aaf.formatid = ab.id
Edit:
I'm not very good with mySql, and don't have anything to test this on, but I think group_concat may be what you are looking for. If so, it will probably look something like this:
select group_concat(ab.name separator ", ") as formats from ab_ads aa
inner join ab_ads_states as aas on aa.id = aas.adid and aas.stateId = 2
inner join ab_ads_formats as aaf on aa.id = aaf.adid and aaf.formatId in(1,2)
inner join ab_formats as ab on aaf.formatid = ab.id
group by ab.id

Please try below SQL:
SELECT count(aaf.ID) AS TotalFormat,
group_concat(ab.name) AS formats
FROM ab_ads aa
INNER JOIN ab_ads_states AS aas ON aa.ID = aas.adID
AND aas.stateID = 2
INNER JOIN ab_ads_formats AS aaf ON aa.id = aaf.adID
AND aaf.formatID in(1,2)
INNER JOIN ab_formats AS ab ON aaf.formatID = ab.ID
GROUP BY aaf.adID HAVING TotalFormat >=2
SQL Demo: http://sqlfiddle.com/#!2/9f0ab/10

Related

MySQL: Select several rows based on several keys from two different tables

I have these two tables - user_schedules and user_schedule_meta, shown below:
------------------------------------
| id | scheduler_id | status |
------------------------------------
1 3 pending
2 5 active
3 6 active
and
----------------------------------------------
| id | user_schedule_id | meta_key |meta_value
----------------------------------------------
1 3 course-id 135
2 3 session-id 15
3 3 schedule-id 120
I want to write a query to enable me select, for example, from both tables where EVERYONE of the below 5 conditions are met:
user_schedule_id = 3
scheduler_id = 6
session_id = 15
course-id = 135
schedule-id = 120
This is what I have so far, but it is not working:
SELECT user_schedule_meta.`id` FROM user_schedule_meta, user_schedules
WHERE user_schedules.`scheduler_id` = 6
AND user_schedules.id = user_schedule_meta.`user_schedule_id`
AND (
(user_schedule_meta.`meta_key` = 'course-id' AND user_schedule_meta.`meta_value` = 135)
OR (user_schedule_meta.`meta_key` = 'session-id' AND user_schedule_meta.`meta_value` = 15)
OR (user_schedule_meta.`meta_key` = 'daily-schedule-id' AND user_schedule_meta.`meta_value` = 120)
)
GROUP BY user_schedule_meta.`id`
Any suggestions what I am not doing right?
This is a typical key-value store lookup problem. These are trickier than they look in SQL, in that they require multiple JOIN operations.
You need a virtual table with one row per user_schedules.id value, then you can filter it. So
SELECT u.id, u.scheduler_id
FROM user_schedules u
JOIN user_schedule_meta a ON u.id=a.user_schedule_id AND a.meta_key='course-id'
JOIN user_schedule_meta b ON u.id=b.user_schedule_id AND b.meta_key='session-id'
JOIN user_schedule_meta c ON u.id=c.user_schedule_id AND c.meta_key='daily-schedule-id'
WHERE a.meta_value = 135 -- value associated with course-id
AND b.meta_value=15 -- value associated with session-id
AND c.meta_value=120 -- value associated with daily-schedule-id
Notice also that you can list your table with associated attributes like this. This trick of joining the key/value table multiple times is a kind of pivot operation. I use LEFT JOIN because it will allow the result set to show rows where an attribute is missing.
SELECT u.id, u.scheduler_id, u.status,
a.meta_value AS course_id,
b.meta_value AS session_id,
c.meta_value AS daily_schedule_id
FROM user_schedules u
LEFT JOIN user_schedule_meta a ON u.id=a.user_schedule_id AND a.meta_key='course-id'
LEFT JOIN user_schedule_meta b ON u.id=b.user_schedule_id AND b.meta_key='session-id'
LEFT JOIN user_schedule_meta c ON u.id=c.user_schedule_id AND c.meta_key='daily-schedule-id'
try this is code
select * from user_schedule_meta where user_schedule_id=3 and
(meta_key='session-id' AND meta_value=15
or meta_key='daily-schedule-id' AND meta_value=120
or meta_key='course-id' AND meta_value=135
)

MySQL query to select specific resources

Scenario:
The school where I work at has already a system that handles resource reservation (racks with tv's, projectors, etc), however I was asked to make a quick-fix for today since the system has a problem, there's no way to make the resource only appear for a specific segment:
1-6th grade is a segment,
7-11th grade is a segment.
The system is made out of PHP and MySQL.
Problem:
Without creating a new table, I have to find a way to show a specific resource only to 1-3rd grade and another one only to 4-6th grade.
Here are the tables:
I added into the grade table a new column called UNIQUERESOURCESwhere I put a value of 1 to sections between first and third grade and a value of 2 to sections between fourth and sixth grade.
How can I show to grades from 1-3 grade ONLY those specific resources and from 4-6 grade ONLY the other specific resources?
Here is the current query:
SELECT DISTINCT r.RESOURCEID, r.RESOURCENAME, rl.RESOURCELOCATIONNAME FROM resource r
inner join resource_location rl
on r.RESOURCELOCATIONID = rl.RESOURCELOCATIONID
inner join grade g
on g.RESOURCELOCATIONID = r.RESOURCELOCATIONID
inner join users_intermediate ui
on g.GRADEID = ui.GRADEID
WHERE ui.USERID = '%s'
How can I fix this query to include the resources as follows:
Include RESOURCELOCATIONID = 5 where UNIQUERESOURCES = 1 if the user is in between GRADEID 1-12
Include RESOURCELOCATIONID = 6 where UNIQUERESOURCES = 2 if the user is in between GRADEID 13-24.
This is the part I can't figure out. Some help would be really appreciated!
EDIT:
Here are some pictures of the resources
Resources:
Grades:
Users Intermediate (emails are censored in this pic):
As you can see, what I'm trying to accomplish is also include the resources that have resourcelocationid = 5 or resourcelocationid = 6 if the user teaches in grades that go from 1-3 or 4-6
Have you tryed this way:
SELECT RESOURCEID, RESOURCENAME, RESOURCEDESCRIPTION,
T3.RESOURCELOCATIONNAME, T2.RESOURCESTATUSNAME,
T2.RESOURCESTATUSDESCRIPTION, T2.RESOURCESTATUSID
FROM resource T1
INNER JOIN resource_status T2 USING(RESOURCESTATUSID)
INNER JOIN resource_location T3 USING(RESOURCELOCATIONID)
INNER JOIN grade USING(RESOURCELOCATIONID)
WHERE T1.RESOURCELOCATIONID IN (
SELECT DISTINCT RESOURCELOCATIONID FROM grade G
INNER JOIN users_intermediate UI
ON G.GRADEID = UI.GRADEID
WHERE USERID = "%s"
)
AND (
T1.RESOURCESTATUSID = 1
OR (
T1.RESOURCELOCATIONID = 5
AND (
UNIQUERESOURCES = 1 AND GRADEID >= 1 AND GRADEID <= 12
)
)
OR (
T1.RESOURCELOCATIONID = 6
AND (
UNIQUERESOURCES = 2 AND GRADEID >= 13 AND GRADEID <= 24
)
)
)
Not seeing sample data and confirming true context of your data and expected output, I would like to mentally describe via transitive association.
User "A" is associated with class/grade 1,2 and 3.
Looking into the Grade table, you find User "A" has grade information pointing to Locations "LH", "LR" and "LX" (arbitrary as to not confuse a location with a grade of 1-3 OR 4-6 (or other as later needed)).
Since we know the qualified "Locations", User "A" has access to ANYTHING found within the "LH", "LR" and "LX" locations.
Now, because the ResourceLocationID is in both the Resource_Location and the Resource table, the Grade table can actually join to each directly as the resource location table is nothing more than a lookup to get a descriptive name of where it is. The multiple things IN the room are the resources.
So, the only thing my inner prequery "QualGroups" does, is get a list of the UniqueResources groups (either 1 or 2) a user has access to. The next level is to get all DISTINCT locations under that 1 or 2 status. Because IF a user is access for group 1 at location "LH" AND class group 2 at location "LH", you do not want duplicated resources for location "LH". I am getting the location name while I am at it. (result alias QualLocations)
Now, I can get the resources for those distinct qualified locations
SELECT
QualLocations.ResourceLocationID,
QualLocations.ResourceLocationName,
R.ResourceID,
R.ResourceName,
R.ResourceDescription,
R.ResourceStatusID
from
( SELECT distinct
RL.ResourceLocationID,
RL.ResourceLocationName
from
grade G2
JOIN ( select distinct
G.UniqueResources
from
users_intermediate UI
JOIN grade G
ON UI.GradeID = G.GradeID
where
UI.UserID = "%s" ) QualGroups
ON G2.UniqueResources = QualGroups.UniqueResources
JOIN Resource_Location RL
ON G2.ResourceLocationID = RL.ResourceLocationID )
as QualLocations
JOIN Resource R
ON QualLocations.ResourceLocationID = R.ResourceLocationID
Now, if you ONLY CARE about a very specific classification group, UPDATE the inner-most query joining the "Grade G" table to include the unique resource you are looking for... in this case either 1 or 2
JOIN grade G
ON UI.GradeID = G.GradeID
AND G.UniqueResource = 1 (or 2 if you only wanted 4th-6th grade)
Hope this helps you on your way.
Aside from posted question above, if you change your "UniqueResource" to have a numeric value of the specific GRADE it is vs the verbiage description of the grade name, the AND clause above could be simplified to something like
AND G.UniqueResource IN ( 1, 2, 3 ) // for only grades 1-3
AND G.UniqueResource BETWEEN 1 and 6 // ex: for grades 1-6
AND G.UniqueResource > 6 // for any grade above 6th grade

select query records if multiple type is matched

I have some mysql tables like below,
1)videos
id name
1 test1
2 test2
2)tags
id name
1 theme1=test1
2 theme1=test2
3 theme2=test1
4 theme2=test2
5 age=senior
6 age=children
3)tags_to_items
vid tagid
1 1
1 5
Here in table tags_to_item you see videos.id=1 has 2 tags
1)theme1=test1 AND 2)age=senior. It means video.id=1 has 2 tags.
Now here is one conflict,
what i am doing now is, i am searching for theme's only. It means if i search where theme1=theme1 then it need to also search theme1 in theme2 and vice versa. like this
WHERE tagid=1 OR tagid=2
This is functioning proper, but now i want to search if video have multiple tag with AND condition like this
WHERE tagid=5 AND tagid IN (1,2)
It should return video.id=1
So, probably it need to search where video 1 contains tag tag age=senior AND theme1=theme1 OR theme2=theme1. But its not working, anyone have idea how can i do like this?
I think i got the answer.
I need to do join like this,
INNER JOIN tags_to_items t2 ON (v.id = t2.tagid AND t2.tagid = 5)
INNER JOIN tags_to_items t3 ON (v.id = t3.tagid AND t3.tagid in ('27' ,'69','84' ,'99' ))

MySQL count datas with row values without new query loop

I've 4 table for a newsletter. Newsletters, Subscribers, Subscriber Groups and Selected Subscriber Groups. I've choose subscriber groups in campaign edit area, and its save selected groups to tbl_newsletter_groups table like;
tbl_newsletters
NID title details
1 text 1 content 1
2 text 2 content 2
tbl_subscriber_groups
GID group_name
5 group 1
6 group 2
tbl_subscribers
SID GID email name
10 5 sub1#mail.com sub1 name
11 6 sub1#mail.com sub1 name
tbl_newsletter_groups
NGID NID GID
15 1 6
16 1 6
17 1 6
I want to show total selected subscriber count when I list newsletters in my page. My soulution works fine, Im looking for simple and clearly statement, there any faster way available like in single newsletter list statement?
Here my own count style (yes I know its too bad and long way);
$subGID = array();
$list = $myconn->query("SELECT * FROM tbl_newsletters");
while($listRs = $list->fetch_assoc()){
$grps = $myconn->query("SELECT * FROM tbl_newsletter_groups WHERE NID=". $listRs['NID'] ."");
while($grpsRs = $grps->fetch_asscoc()){
$subGID[] = $grpsRs['GID'];
} $grps->free();
$subs = implode(" OR GID=",$subGID);
$count = mysqli_num_rows($myconn->query("SELECT ID FROM tbl_subscribers WHERE GID=". $subs));
echo('Total Selected Subscriber: '.$count);
} $list->free();
Thanks.
The search term you want is "set-based logic".
Your thinking is sound: you need everything from tbl_newsletters, then you need to count results from tbl_subscribers, but in order to get those you need information from tbl_newsletter_groups.
In SQL, that's an indication you want a join. You've already discovered the conditions you need, you just don't know the syntax. A reference manual can help there.
Now you'll have a bunch of records, which you need to smash into a smaller number of records. You need aggregation functions and a GROUP BY clause.
So here's the final query:
SELECT n.NID, n.title, n.details, COUNT(s.SID)
FROM tbl_newsletters AS n
JOIN tbl_newsletter_groups AS g ON n.NID = g.NID
JOIN tbl_subscribers AS s ON g.GID = s.GID
GROUP BY n.NID

select returns not righ result

I have a table :
Person Language
6 1
6 2
6 3
7 1
7 2
I would like to select the person who speaks the language 1 AND 2 AND 3 (person 6)
I coded this query, but I don't get the right result :
SELECT guides.id, guides.nom, guides.prenom, langues.langue
FROM guides
JOIN guides_has_langues ON guides_has_langues.guides_id = guides.id
JOIN langues ON guides_has_langues.langues_id = langues.id
WHERE guides_has_langues.langues_id = 1 AND guides_has_langues.langues_id = 2 AND guides_has_langues.langues_id = 3
I think that this query select field that are 1 AND 2 AND 3, is it right ?
It's not my goal.
Select all entries for languages 1, 2 and 3. Then group by guide and stay with those having all three languages (using the HAVING clause). To show the languages use the function your dbms offers to concatenate them (e.g. GROUP_CONCAT for MySQL).
SELECT guides.id, guides.nom, guides.prenom, GROUP_CONCAT(langues.langue)
FROM guides
JOIN guides_has_langues ON guides_has_langues.guides_id = guides.id
JOIN langues ON guides_has_langues.langues_id = langues.id
WHERE guides_has_langues.langues_id IN (1,2,3)
GROUP BY guides.id, guides.nom, guides.prenom
HAVING COUNT(DISTINCT guides_has_langues.langues_id) = 3;
(If you want one line per guide and language this will be a little more complicated, because you need the raw entires plus the aggregation information on how many languages the guide speaks.)

Categories