I'll try to explain the problem as good as possible.
I have two tables "users1213" which is a table of players and "Loanperiodes". Now I'm trying to get immediately with a sql-query a list of players who is at the moment at the club. If they are on loan to another club they don't have to be on the list. It's also possible that the club is loaning a player so these players have to be on the list as well. So I have this for the moment (the club has an id "1" and the team has also an id "1"):
SELECT users1213.id, surname, name, team, club, loanclub
FROM users1213
LEFT JOIN loanperiode ON users1213.id = loanperiode.player
WHERE users1213.status = 'player'
AND (
(
users1213.club != '1'
AND loanperiode.loanclub = '1'
AND loanperiode.begin <= '2013-02-03'
AND loanperiode.end >= '2013-02-03'
AND (
uitleenperiodes.team_id = '1'
)
)
OR (
users1213.club = '1'
AND users1213.team = '1'
AND (
loanperiode.loanclub IS NULL
OR (
loanperiode.loanclub IS NOT NULL
AND (
loanperiode.begin < '2013-02-03'
AND loanperiode.end < '2013-02-03'
)
OR (
uitleenperiodes.begin > '2013-02-03'
AND uitleenperiodes.end > '2013-02-03'
)
)
)
)
)
GROUP BY users1213.id
ORDER BY name
LIMIT 0 , 30
With this query I get a good result but there is 1 problem, when someone had got 2 loanperiodes. For example PLAYER1 is loaned now to CLUB A so he may not be listed. But when PLAYER1 has also got a loanperiode which has already expired, to CLUB B, PLAYER1 will be listed because of the LEFT JOIN. Is there a solution to solve this with a query or must I check afterwards when I'm running the array?
Many thanks!
SELECT users1213.id, surname, name, team, club, loanclub
FROM users1213
LEFT JOIN loanperiode ON users1213.id = loanperiode.player
AND '2013-02-03' BETWEEN loanperiode.begin AND loanperiode.end
WHERE
loanperiode.loanclub = '1'
OR (loanperiode.loanclub IS NULL -- I suppose this happens only when there is no loan between those dates so nothing LEFT JOINed
AND users1213.club = '1')
ORDER BY name
This example supposes that user can have only one loan at once (yes, he can have more loans ended in history, but only one begins before today and ends in the future).
Related
I have one table where I would like to count the amount of names grouped by school, count whether school has one or two genders as well as count the specific occurence of a particular value in 3 different columns. I am able to do the counts in two different tables but I want to join them to make 1 table
FName
School
Gender
Events
Events2
Events3
Ann
Marymount
F
HJ
LJ
TJ
Peter
Marymount
M
100
200
400
Drew
St Hughs
M
100
200
Davis
St Hughs
M
200
Kat
Campion
F
400
Molly
Campion
F
400
Mike
Marymount
M
800
Fran
Campion
M
100
200
These are the 2 separate queries I use successfully and create two different result sets
$sql = "SELECT COUNT(FName) as cnt, COUNT(DISTINCT(Gender)) as gnd, school, COUNT(*) FROM entries WHERE school <> 'Unattached' GROUP BY school";
$sql = "SELECT COUNT(Events + Events2 + Events3) as events, school, COUNT(*) FROM entries WHERE (school <> 'Unattached') AND (Events = '100') OR (Events2 = '100') OR (Events3 = '100') GROUP BY school";
$result = $conn->query($sql);
I have tried using a union but it only gives me results for second query and the count of names is off.
$sql = "
SELECT COUNT(FName) as cnt, COUNT(Events + Events2 + Events3) as events, COUNT(DISTINCT(Gender)) as gnd, school
FROM entries WHERE (school <> 'Unattached')
UNION
SELECT COUNT(FName) as cnt, COUNT(Events + Events2 + Events3) as events, COUNT(DISTINCT(Gender)) as gnd, school
FROM entries WHERE (school <> 'Unattached') AND (Events = '100') OR (Events2 = '100') OR (Events3 = '100')
GROUP BY school";
$result = $conn->query($sql);
The left join gave me no results because I didn't know how to fit the 2 WHERE clauses.
How can I join the two queries to get one table with the following result
School
# of Participants
# Genders
# 100
Marymount
3
2
1
St Hughs
2
1
1
Campion
3
2
1
Use SUM() to add up the number of rows where a condition is true. This works because a boolean value is 1 when it's true, 0 when it's false, so SUM() gets this count.
SELECT school,
COUNT(*) AS num_participants,
COUNT(DISTINCT gender) AS num_genders,
SUM(events = '100' OR events2 = '100' OR events3 = '100') AS num_100
FROM entries
WHERE school != 'Unattached'
GROUP BY school
It is kind of challenge. The expected output, all fruits are from ny or ak state and if the mark good=price 10 and bad= price 20 and OK= price 40. So, apple from ny state, have mark good and bad match the price 10 and 20, and apple don't have mark OK row, so no need to compare that. Banana mark bad = price 20, but OK is not equal price 40, so I will rule out banana. pear match OK = price 40 and state=ny and no rows of good and bad mark. Therefore, my output should be apple and pear. How should I do that?
<?php
/*
fruit state price mark
apple ny 10 good
apple ny 20 bad
banana ny 20 bad
banana ny 30 OK
pear ny 40 OK
berry pa 10 good
*/
/*this is my best query but fail to do what I expected*/
SELECT fruit FROM table WHERE state IN ('ny','ak') AND mark IN ('good', 'bad', 'OK') AND price IN ( '10','20','40') GROUP BY fruit
?>
you should where and or condition
You should use a pair of not in
select distinct fruit from my_table
where fruit not in (select distinct fruit from my_table
where (mark, price) not in (select 'good', 10 from dual
union
select 'bad, 20 from dual
union
select 'OK', 40 from dual))
state IN ('ny','ak') ;
The list of potential fruit that could be returned, because a row matches the specification:
SELECT t.fruit
FROM `table` t
WHERE t.state IN ('ny','ak')
AND ( ( t.mark = 'good' AND t.price = 10 )
OR ( t.mark = 'bad' AND t.price = 20 )
OR ( t.mark = 'OK' AND t.price = 40 )
)
GROUP BY t.fruit
A list of fruit that we don't want to return, because there is a row that doesn't satisfy the specification:
SELECT t.fruit
FROM `table` t
WHERE t.state IN ('ny','ak')
AND NOT ( t.mark = 'good' AND t.price = 10 )
AND NOT ( t.mark = 'bad' AND t.price = 20 )
AND NOT ( t.mark = 'OK' AND t.price = 40 )
GROUP BY t.fruit
Note that this will also include rows where the mark column has a value other than good, bad or OK. If we want to ignore rows with other values of mark, then we'd add...
AND t.mark IN ('good','bad','OK')
That gives us two sets. We can combine those with an antijoin operation. As an example of what that looks like, if we had tables fruit_to_include and fruit_to_exclude ...
SELECT i.fruit
FROM fruit_to_include i
LEFT
JOIN fruit_to_exclude e
ON e.fruit = i.fruit
WHERE e.fruit IS NULL
ORDER BY i.fruit
We can replace those placeholder table names with inline views, using the queries that give us the fruit to include and fruit to exclude. The only difference here is replacing the dummy table name with (what MySQL refers to as) derived table.
SELECT i.fruit
FROM -- fruit_to_include
( SELECT t.fruit
FROM `table` t
WHERE t.state IN ('ny','ak')
AND ( ( t.mark = 'good' AND t.price = 10 )
OR ( t.mark = 'bad' AND t.price = 20 )
OR ( t.mark = 'OK' AND t.price = 40 )
)
GROUP BY t.fruit
) i
LEFT
JOIN -- fruit_to_exclude
( SELECT r.fruit
FROM `table` r
WHERE r.state IN ('ny','ak')
AND r.mark IN ('good','bad','OK')
AND NOT ( r.mark = 'good' AND r.price = 10 )
AND NOT ( r.mark = 'bad' AND r.price = 20 )
AND NOT ( r.mark = 'OK' AND r.price = 40 )
GROUP BY r.fruit
) e
ON e.fruit = i.fruit
WHERE e.fruit IS NULL
ORDER BY i.fruit
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
Lets say there is a social network and i want to show my logged users latest activities of his groups/friends/family members
I have all activities in the registry table and 3 different level for each activity
0 => every one can see it
1 => only friends or in case of group, members can see it
2 => only family members can see it
Also i have 2 arrays containing logged user friends_groups AND family
select * from registry where user_id in (implode(',' , $groups_friends_array) ) && level < 2
union
select * from registry where user_id in (implode(',' , $family_array) ) && level < 3
Is there any way to somehow do this without using union maybe with one select?
You can combine the two WHERE clause using OR
SELECT...
FROM...
WHERE (user_id in (implode(',' , $groups_friends_array)) AND level < 2) OR
(user_id in (implode(',' , $family_array)) AND level < 3)
select * from registry
where
(
user_id in (implode(',' , $groups_friends_array) ) and level < 2
)
or
(
user_id in (implode(',' , $family_array) ) and level < 3
)
My query below will get the last 3 active threads from my blog's commnets,
SELECT ID, Approved, RecipientID, RecipientScreenname, RecipientEmail
FROM
(
SELECT
root_strings.str_id as ID,
root_strings.str_approved as Approved,
root_strings.mem_id as RecipientID,
root_members_cfm.mem_screenname as RecipientScreenname,
root_members_cfm.mem_firstname as RecipientFirstname,
root_members_cfm.mem_email as RecipientEmail
FROM root_strings
LEFT JOIN root_members_cfm
ON root_members_cfm.mem_id = root_strings.mem_id
WHERE root_strings.parent_id = '1'
AND root_strings.mem_id IS NOT NULL
UNION ALL
SELECT
root_strings.str_id as ID,
root_strings.str_approved as Approved,
root_strings.mem_id as RecipientID,
root_users.usr_screenname as RecipientScreenname,
root_users.usr_firstname as RecipientFirstname,
root_users.usr_email as RecipientEmail
FROM root_strings
LEFT JOIN root_users
ON root_users.usr_id = root_strings.usr_id
WHERE root_strings.parent_id = '1'
AND root_strings.usr_id IS NOT NULL
) SQ
ORDER BY ID DESC
LIMIT 0,3
It returns a result like this,
ID Approved RecipientID RecipientScreenname RecipientEmail
14 1 3 x x#yahoo.co.uk
13 n/a NULL y y#yahoo.co.uk
13 n/a NULL y y#yahoo.co.uk
Then I will an email to each of them.
foreach($items_thread as $item_thread)
{
$sentmail = mail($item_thread['RecipientEmail'],$email_subject,$email_content,$email_headers);
}
But the logic is not correct when you look closer as I will send y twice of the email!
y should just get one email. How can I fix it - should I fix the sql query or the php code?
The problem is that NULL!=NULL (nor is NULL=NULL), so DISTINCT does not consider the 2nd and 3rd results to be the same. If you change your query so that RecipientID shows as 0 (e.g COALESCE(RecipientID,0) AS RID) the problem will go away.
I have a solution for this now which is using array_unique, here it is Remove duplicate items from an array