Selecting blackout dates through LEFT JOIN and WHERE selector - php

Been working on this query for some time and I'm having trouble with a selector. I have a properties table and a dates tables. The dates table contains dates (in a DATE and TIMESTAMP format) that are NOT AVAILABLE for the property. The WHERE clause only selects properties that have entries. This is a problem because it should also select properties that have zero entries, because they are 100% available to rent.
Here is a basic query:
SELECT p.*, p.land_id AS landCode, p.id AS propertyId, d.*, COUNT(d.id) AS land
FROM `properties` AS p
LEFT JOIN `dates` AS d ON `p`.`id` = `d`.`land_id`
WHERE (`d`.`timestamp` BETWEEN '1283317200' AND '1285909199')
GROUP BY `p`.`id `
ORDER BY `land` ASC
This only returns 1 property (because it has half of September blacked out) and not the other 2 which do not have any dates in September blacked out. If I leave out the WHERE clause, then it will return all properties like I need, but then the date range is not restricted by the user's search.

Try putting the criteria within the JOIN
LEFT JOIN `dates` AS d ON `p`.`id` = `d`.`land_id`
AND (`d`.`timestamp` BETWEEN '1283317200' AND '1285909199')
This should allow all properties to be returned but will only join the ones that are within the date range.

Thanks for you help guys. I decided to add the selector to the actual JOIN instead of the entire query and IT WORKED!
SELECT p.*, p.land_id AS landCode, p.id AS propertyId, d.*, COUNT(d.id) AS land
FROM `properties` AS p
LEFT JOIN `dates` AS d ON `p`.`id` = `d`.`land_id` AND `d`.`timestamp` BETWEEN '1283317200' AND '1285909199'
GROUP BY p.id
ORDER BY land ASC
This would be the correct query. Thanks again!!

Try this:
WHERE (d.timestamp IS NULL OR d.timestamp between '1283317200' and '1285909199')

You need to add an "or" expression, directly in the left join. Like this:
SELECT p.*, p.land_id AS landCode, p.id AS propertyId, d.*, COUNT(d.id) AS land
FROM `properties` AS p
LEFT JOIN `dates` AS d ON `p`.`id` = `d`.`land_id` and d.timestamp between '1283317200' AND '1285909199'
GROUP BY `p`.`id `
ORDER BY `land` ASC
Edit: changed the null check so that it is directly (implicitly) in the outer join.

Related

Creating Forum, and Trying to Sort two tables by timestamp. One table Topics and other Replys. How can I sort these?

I am trying to sort two tables that I created for a forum by timestamp.
I have searched with no luck yet. Please help.
My first table is "forum_posts" and timestamp is "forum_timestamp"
My second table is "forum_posts_replys" is "fpr_timestamp"
With the following query I am able to sort by timestamp or the other, but I would like to sort both. If there are not any replys in a topic yet and it was just posted I would like to see it on the top, but if there is a reply in another topic I would like that reply to show first.
(SELECT fp.*, fpr.*
FROM forum_posts fp
LEFT JOIN forum_posts_replys fpr
ON fp.forum_post_id = fpr.fpr_post_id
WHERE `fp`.`forum_id`='$f_id'
GROUP BY fp.forum_post_id)
ORDER BY `forum_timestamp` DESC
I also tried GREATEST() and no luck.
The page I am trying to use this on is the main topic listing page. The page just shows topics with their titles and a count of replys and how long ago each was posted.
My current query 06/14/14:
SELECT sub.*
FROM
(SELECT
fp.forum_post_id as forum_post_id, fp.forum_id as forum_id,
fp.forum_user_id as forum_user_id, fp.forum_title as forum_title,
fp.forum_content as forum_content, fp.forum_edit_date as forum_edit_date,
fp.forum_timestamp as forum_timestamp, fpr.id as id,
fpr.fpr_post_id as fpr_post_id, fpr.fpr_id as fpr_id,
fpr.fpr_user_id as fpr_user_id, fpr.fpr_title as fpr_title,
fpr.fpr_content as fpr_content, fpr.fpr_edit_date as fpr_edit_date,
fpr.fpr_timestamp as fpr_timestamp,
ifnull(fpr.fpr_timestamp,forum_timestamp) as tstamp
FROM forum_posts fp
LEFT JOIN forum_posts_replys fpr
ON fp.forum_post_id = fpr.fpr_post_id
WHERE `fp`.`forum_id`='$f_id'
GROUP BY fp.forum_post_id) sub
ORDER BY tstamp DESC
Still only seems to sort by forum_timestamp
Use a subquery.
SELECT fp.*, fpr.*
from
(SELECT fp.*, fpr.*, ifnull(fpr.fpr_timestamp,forum_timestamp) as tstamp
FROM forum_posts fp
LEFT JOIN forum_posts_replys fpr
ON fp.forum_post_id = fpr.fpr_post_id
WHERE `fp`.`forum_id`='$f_id'
GROUP BY fp.forum_post_id) sub
order by tstamp desc
Edit: List the wanted fields explicitly and use the table alias for the subquery:
SELECT sub.*
from
(SELECT fp.field_1 as f1, fp.field_2 as f2, fpr.field_1 as f3, fpr.field_2 as f4, ifnull(fpr.fpr_timestamp,forum_timestamp) as tstamp
FROM forum_posts fp
LEFT JOIN forum_posts_replys fpr
ON fp.forum_post_id = fpr.fpr_post_id
WHERE `fp`.`forum_id`='$f_id'
GROUP BY fp.forum_post_id) sub
order by tstamp desc
With your help I was able to troubleshoot this to make it work!
Here is the final query that worked flawlessly.
SELECT sub.*
FROM
(SELECT
fp.forum_post_id as forum_post_id, fp.forum_id as forum_id,
fp.forum_user_id as forum_user_id, fp.forum_title as forum_title,
fp.forum_content as forum_content, fp.forum_edit_date as forum_edit_date,
fp.forum_timestamp as forum_timestamp, fpr.id as id,
fpr.fpr_post_id as fpr_post_id, fpr.fpr_id as fpr_id,
fpr.fpr_user_id as fpr_user_id, fpr.fpr_title as fpr_title,
fpr.fpr_content as fpr_content, fpr.fpr_edit_date as fpr_edit_date,
fpr.fpr_timestamp as fpr_timestamp,
GREATEST(fp.forum_timestamp, COALESCE(fpr.fpr_timestamp, '00-00-00 00:00:00')) AS tstamp
FROM forum_posts fp
LEFT JOIN forum_posts_replys fpr
ON fp.forum_post_id = fpr.fpr_post_id
WHERE fp.forum_id=$f_id
ORDER BY tstamp DESC
) sub
GROUP BY forum_post_id
ORDER BY tstamp DESC

mysql group_concat returns null row if one entry in the group is null

I have a rather simple query which looks to combine all results from another table into one json object.
MySQL
SELECT s.id, s.uid, s.sexnumber, s.rating, s.sextime, s.diary, GROUP_CONCAT(p.pid,',') as positions, GROUP_CONCAT(w.id,':',w.who) as who, GROUP_CONCAT('{',
'\"lat\":',l.lat,
',\"lon\":',l.lon,
',\"house\":',l.house,
',\"house_number\":',l.house_number,
',\"road\":',l.road,
',\"supermarket\":',l.supermarket,
',\"city\":',l.city,
',\"city_district\":',l.city_district,
',\"country\":',l.country,
',\"country_code\":',l.country_code,
',\"county\":',l.county,
',\"neighbourhood\":',l.neighbourhood,
',\"pedestrian\":',l.pedestrian,
',\"place_of_worship\":',l.place_of_worship,
',\"postcode\":',l.postcode,
',\"state\":',l.state,
',\"suburb\":',l.suburb,
'}'
) as location, GROUP_CONCAT(ww.id,':',ww.name) as place
FROM users u join sex s
on s.uid = u.uid
LEFT OUTER JOIN whos ws
ON s.id = ws.sid
LEFT OUTER JOIN who w
ON w.id = ws.wid
LEFT OUTER JOIN locations l
ON l.sid = s.id
LEFT OUTER JOIN wheresex whs
ON whs.sid = s.id
LEFT OUTER JOIN wherewhere ww
ON whs.wid = ww.id
LEFT OUTER JOIN positions p
ON s.id = p.sid
WHERE u.sessionCheck = '%s'
GROUP BY s.id
ORDER BY s.sextime DESC;
If any of the location results inside the GROUP_CONCAT are NULL then the entire entry will return NULL.
How can I have the individual results that are NULL return this way and anything else with their values?
I must admit that I haven't tried to execute this sort of query before, but I can't see how a coalesce wouldn't do the trick?
SELECT s.id, s.uid, s.sexnumber, s.rating, s.sextime, s.diary, GROUP_CONCAT(p.pid,',') as positions, GROUP_CONCAT(w.id,':',w.who) as who, GROUP_CONCAT('{',
'\"lat\":',coalesce(l.lat,'Unknown'),
',\"lon\":',coalesce(l.lon,'Unknown'),
',\"house\":',coalesce(l.house,'Unknown'),
',\"house_number\":',coalesce(l.house_number,'Unknown'),
',\"road\":',coalesce(l.road,'Unknown'),
',\"supermarket\":',coalesce(l.supermarket,'Unknown'),
',\"city\":',coalesce(l.city,'Unknown'),
',\"city_district\":',coalesce(l.city_district,'Unknown'),
',\"country\":',coalesce(l.country,'Unknown'),
',\"country_code\":',coalesce(l.country_code,'Unknown'),
',\"county\":',coalesce(l.county,'Unknown'),
',\"neighbourhood\":',coalesce(l.neighbourhood,'Unknown'),
',\"pedestrian\":'coalesce(,l.pedestrian,'Unknown'),
',\"place_of_worship\":',coalesce(l.place_of_worship,'Unknown'),
',\"postcode\":',coalesce(l.postcode,'Unknown'),
',\"state\":',coalesce(l.state,'Unknown'),
',\"suburb\":',coalesce(l.suburb,'Unknown'),
'}'
) as location, GROUP_CONCAT(ww.id,':',ww.name) as place
Edit: I am not sure if I understand your comment correctly, but if you want to add quotes around the values returned by the coalesce you could use a concat function like this:
'\"lat\":',concat('"',coalesce(l.lat,'Unknown'),'"')
You maybe looking for Concat not Group_Concat
CONCAT(w.id,':',w.who)
and same for
CONCAT('{',
'\"lat\":',l.lat,
',\"lon\":',l.lo

How to write JOIN QUERY for 4 tables in the below condition

I have 4 tables ACCOUNTS_TABLE , LINKS_TABLE, GROUPS_TABLE, KEYS_TABLE
I need to get all accounts details which is of acct_type xx with count of Links, groups& keywords . I have tried this query but it gives all count as 0
SELECT
acc.acct_id, acc.acct_type, count(link.id) as link_count, link.account,
groups.camp_id, count(groups.id) as group_count, count(keyword.key_id) as key_count
FROM ".ACCOUNTS_TABLE." as acc
LEFT JOIN ".LINKS_TABLE." as link ON link.account=acc.acct_id AND acct_type='xx'
LEFT JOIN ".GROUPS_TABLE." as groups ON groups.camp_id=link.id
LEFT JOIN ".KEYS_TABLE." as keyword ON keyword.camp_id=link.id
GROUP BY acc.acct_id
My required output should be like this
Any one please help me to slove this problem
You probably should use COUNT(DISTINCT ....).
SELECT acc.acct_id, COUNT(DISTINCT link.id), COUNT(DISTINCT groups.id), COUNT(DISTINCT keyword.key_id)
FROM ACCOUNTS_TABLE acc
LEFT OUTER JOIN LINKS_TABLE link ON link.account = acc.acct_id AND acct_type = 'advertiser'
LEFT OUTER JOIN GROUPS_TABLE groups ON groups.camp_id = link.id
LEFT JOIN KEYS_TABLE keyword ON keyword.id = link.id
WHERE acc.acct_type = 'xx'
GROUP BY acc.acct_id
EDIT
Amended to use the updated join conditions, etc:-
SELECT acc.acct_id, acc.acct_type, COUNT( DISTINCT link.id ) , COUNT( DISTINCT groups.id ) , COUNT( DISTINCT keyword.key_id )
FROM ACCOUNTS_TABLE acc
LEFT OUTER JOIN LINKS_TABLE link ON link.account = acc.acct_id
LEFT OUTER JOIN GROUPS_TABLE groups ON groups.camp_id = link.id
LEFT JOIN KEYS_TABLE keyword ON keyword.camp_id=link.id
WHERE acc.acct_type = 'xx'
GROUP BY acc.acct_id, acc.acct_type
You could try something like this:
SELECT ACC.Id
,( SELECT COUNT (*) FROM Links L WHERE L.AccountId = ACC.Id ) AS CountOfLinks
,( SELECT COUNT (*) FROM Groups G WHERE G.AccountId = ACC.Id ) AS CountOfGroups
FROM ( SELECT Id FROM Accounts Acc WHERE Acc.Type = 'some type' ) ACC
I've rejigged your code a bit (see below) for a few reasons:
It's helpful (for me anyway) to write my SELECT statements always in a certain way - with anything that is not being grouped placed first, and ideally putting things in same order as my JOINs and doing the same in my GROUP BY
I put anything which restricts my FROM table into the WHERE not the JOIN to make it clearer what I'm trying to do and also to make it easier to modify later on.
I also like to ensure it's well laid out to make it easier to scan for issues.
Take this rearranged query and read through it to make sure you are getting the behaviour you're expecting.
PS I'm not sure about your table names and quotation style - I usually use back ticks (`) and would never put dots (.) in my table names. If you put these in as placeholders that's fine but they could lead to trouble for you if they are real.
SELECT
acc.acct_id,
-- if you don't group by these then you need to remove them as they will just return the first values based on mysql behaviour
acc.acct_type,
link.account,
groups.camp_id,
-- these counts will only count where an ID is present which seems like what you're after
count(link.id) as link_count,
count(groups.id) as group_count,
count(keyword.key_id) as key_count
FROM ".ACCOUNTS_TABLE." as acc
LEFT JOIN ".LINKS_TABLE." as link ON link.account=acc.acct_id
LEFT JOIN ".GROUPS_TABLE." as groups ON groups.camp_id=link.id
LEFT JOIN ".KEYS_TABLE." as keyword ON keyword.id=link.id
WHERE acct_type='advertiser'
GROUP BY acc.acct_id,
-- only use these if you intend to group by them
acc.acct_type,
link.account,
groups.camp_id DESC
SELECT acct_type,
count(acct_type),
count(l.id),
count(g.id),
count(key_id)
FROM accounts a
LEFT JOIN links l ON (l.account = a.acct_id)
LEFT JOIN groups g ON (g.camp_id = l.id)
LEFT JOIN keysTable k ON k.group_id = g.id
GROUP BY acct_type HAVING acct_type = 'xx';
SQL Fiddle Validated: http://www.sqlfiddle.com/#!2/f4b6a/20
SELECT
accounts_table.acct_id,
accounts_table.acct_type,
COUNT(DISTINCT links_table.id) AS link_count,
COUNT(DISTINCT groups_table.id) AS group_count,
COUNT(DISTINCT keys_table.key_id) AS key_count
FROM
accounts_table
LEFT JOIN
links_table
ON links_table.account = accounts_table.acct_id
LEFT JOIN
groups_table
ON groups_table.camp_id = links_table.id
LEFT JOIN
keys_table
ON keys_table.camp_id = links_table.id
WHERE
acct_type = 'xx'
GROUP BY
accounts_table.acct_id,
accounts_table.acct_type
ORDER BY
link_count DESC,
group_count DESC,
key_count DESC
Edited answer to match updated question - this should do what you've asked for.
This should do what you've asked for, SQL fiddle here - http://www.sqlfiddle.com/#!2/f4b6a/20

Issue with group by and order by clause with inner join

I have two tables namely locations and pilots. I have been trying to fetch data based on location_id i.e selecting pilots who are flying in a particular location_id order by date(date of schedule).
I am using group by as i need only distinct pilots to be displayed.
select B.*,
A.rather_to_be_flying_now,
A.here_now,
A.flying,
A.when,
A.my_favorite,
A.start,
A.end,
A.Locationid
from locations A
inner join pilots B
on A.Pilotid=B.pilot_id
where A.VenueID='$venueid'
and (A.flying='1' or A.here_now='1')
group by A.Pilotid
ORDER BY A.start
The query works good if i wont include a group by clause. It returns the following result
with out group by clause
with group by clause
But the above table shows wrong order, as the output must return start time as 2013-01-24 02:00:00 for pilotid 1 (Chronological order).
You can use MIN()
select B.*,
A.rather_to_be_flying_now,
A.here_now,
A.flying,
A.when,
A.my_favorite,
MIN(A.start) as start,
A.end,
A.Locationid
from locations A
inner join pilots B
on A.Pilotid=B.pilot_id
where A.VenueID='$venueid'
and (A.flying='1' or A.here_now='1')
group by A.Pilotid
ORDER BY A.start
Try this instead:
SELECT
B.*,
A.rather_to_be_flying_now,
A.here_now,
A.flying,
A.when,
A.my_favorite,
A.start,
A.end,
A.Locationid
FROM locations A
INNER JOIN
(
SELECT pilotid, MIN(start) MinStart
FROM locations
GROUP BY pilotid
) a2 ON A.pilotId = a2.pilotId
AND a.start = a2.minStart
INNER JOIN pilots B on A.Pilotid = B.pilot_id
WHERE A.VenueID = '$venueid'
AND (A.flying='1' OR A.here_now='1');
ORDER BY A.start ;
This will give you only those pilots with the minimum start date.
Try this query -
SELECT
p.pilit_id, l.location_id, l.start
FROM pilots p
JOIN (SELECT l1.*
FROM locations l1
JOIN (SELECT location_id, MIN(start) start
FROM locations
GROUP BY locations) l2
ON l1.id = l2.id AND l1.start = l2.start
) l
ON l.pilot_id = p.pilot_id
GROUP BY p.pilot_id
Add your WHERE condition.

MySQL Where to put in this statement?

This is a bit beyond my skills and I had a lot of help from the good people at SO to get this far. What I need now is to put in a MATCH() ... AGAINST() but I don't know where to insert it?
The query I have is (and this is the short version):
SELECT
SQL_CALC_FOUND_ROWS i.idItems RowCount,
i.* Items,
# Create a JSON formatted field
CONCAT('{',GROUP_CONCAT('"',Attributes.key, '":"', CONVERT(Attributes.value,CHAR),'"'),'}') as Attributes,
IF (te.Key IS NULL,tp.Key,te.Key) as Type,
tc.Value Color,
l.* Location,
c.Name,
c.Mobile,
c.Mail
FROM
(SELECT ItemID, ats.Key, ats.Value FROM attributeStrings as ats
UNION ALL
SELECT ItemID, ati.Key, ati.Value FROM attributeIntegers as ati
) Attributes
JOIN Items i ON
i.idItems = Attributes.ItemID
AND CheckIn >= DATE_SUB('2011-02-16 00:00:00',INTERVAL 90 DAY)
AND CheckIn <= DATE_ADD('2011-02-16 23:59:59',INTERVAL 90 DAY)
AND Checkout IS NULL
LEFT JOIN Customers c ON c.idCustomers = i.CustomerID
LEFT JOIN attributeintegers atli ON atli.itemid = i.idItems AND atli.key = 'Location'
LEFT JOIN locations l ON l.id = atli.value
LEFT JOIN attributestrings atts ON atts.itemid = i.idItems AND atts.key = 'Type' LEFT
JOIN Lists tp ON tp.value = atts.value
LEFT JOIN attributestrings attes ON attes.itemid = i.idItems AND attes.key = 'Tech' LEFT
JOIN Lists te ON te.value = attes.value
LEFT JOIN attributeintegers atci ON atci.itemid = i.idItems AND atci.key = 'Color' LEFT
JOIN Strings tc ON tc.StringID = atci.value
GROUP BY Attributes.ItemID
ORDER BY CheckIn DESC
Now I need to get this statement in here somewhere
MATCH(attributestrings.Value) AGAINST("Nokia" IN BOOLEAN MODE)
As you can see there is a table called attributestrings and it has 3 columns: ItemID,*Key* and Value. I need to search the column Value for the words in the AGAINST() and only show results matching this and the other criterias such as the Date and Checkout above.
I tried to add the statement after the AND Checkout IS NULL like this:
AND Checkout IS NULL
AND MATCH(Attributes.Value) AGAINST("Nokia" IN BOOLEAN MODE)
I had to use the Attributes.Value instead of attributestrings because it didn't found the table. This only resulted in the CONCATENATED column Attributes only contained the value "Nokia", even if there where more to CONCATENATE.
I hope someone are willing to take on this challenge...
// Tank you.
[EDIT]
I tried to put in the WHERE before the GROUP as Tim Fultz sugested, but I get the error
Unknown column 'attributestrings.Value' in 'Where clause'
LEFT JOIN attributeintegers atci ON atci.itemid = i.idItems AND atci.key = 'Color' LEFT JOIN Strings tc ON tc.StringID = atci.value
WHERE MATCH(attributestrings.Value) AGAINST("Nokia Sony" IN BOOLEAN MODE)
GROUP BY Attributes.ItemID
Typically this is put in the Where clause:
WHERE MATCH(attributestrings.Value) AGAINST("Nokia" IN BOOLEAN MODE)
I think I came up with a solution...
I added another:
LEFT JOIN attributestrings fts ON fts.itemid = i.idItems AND MATCH(fts.Value) AGAINST("Nokia Sony" IN BOOLEAN MODE)
Just before the GROUP BY... and then included fts.Value ftsv in the main select statement. Now I could insert a HAVING ftsv IS NOT NULL between GROUP BY... and ORDER BY...
This gave me the result I wanted but the query starts to get a bit slow now...
There is a problem in your query when you are assigning table names with as use every where in you query the name you assigned. Like you gave attributestrings as ats now use ats everywhere and this will work.

Categories