MySQL Where to put in this statement? - php

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.

Related

Query is not showing up 0 values mysql

In my query I am listing all of the theater ticket sales and movie ticket sales of different customers. The issue I'm running into is that all of the '0' ticket sales, so those users who haven't boughten a theater ticket or movie ticket is not showing up.
Here's a picture for a visual aspect: table
I believe I need to be doing a union to return the users who haven't boughten any tickets. I just can't seem to figure this out.
Thanks in advance.
Here's my code so far:
select customer.hippcode, customer.LastName, customer.Firstname, customer.Email,
count(ticketdetails.eventtype) as 'Theater Tickets',
0 as 'Movie Tickets'
from customer
inner join ticketdetails on ticketdetails.hippcode = customer.hippcode
where ticketdetails.hippcode is not null
and ticketdetails.eventType ='T'
Group by Customer.hippcode
union
select customer.hippcode, customer.LastName, customer.Firstname, customer.Email,
0 as 'Theater Tickets', count(ticketdetails.eventtype) as 'Movie Tickets'
from customer
inner join ticketdetails on ticketdetails.hippcode = customer.hippcode
where ticketdetails.hippcode is not null
and ticketdetails.eventType ='M'
Group by Customer.hippcode
order by `theater tickets` + `movie tickets` desc;
select
customer.hippcode, customer.LastName, customer.Firstname, customer.Email,
sum(case when ticketdetails.eventtype = 'T' then 1 else 0 end) as TheaterTickets,
sum(case when ticketdetails.eventtype = 'M' then 1 else 0 end) as MovieTickets
from customer
inner join ticketdetails on ticketdetails.hippcode = customer.hippcode
where ticketdetails.hippcode is not null
and ticketdetails.eventType in ('T', 'M')
Group by customer.hippcode, customer.LastName, customer.Firstname, customer.Email
Order by 'TheaterTickets' + 'MovieTickets' desc
inner join => bring the line only if you got a record on both table.
I think you should use a LEFT JOIN somewhere to chose the master table
http://dev.mysql.com/doc/refman/5.7/en/join.html and
http://dev.mysql.com/doc/refman/5.7/en/left-join-optimization.html
I think the last query is the only one you want. A left join is appropriate, but you need to be careful about the where clause:
select c.hippcode, c.LastName, c.Firstname, c.Email,
sum(td.eventtype) as TheaterTickets,
sum(td.eventtype) as MovieTickets
from customer c left join
ticketdetails td
on td.hippcode = c.hippcode and
td.eventType in ('T', 'M')
Group by c.hippcode, c.LastName, c.Firstname, c.Email
Order by count(t.hippcode) desc;
Notes:
Table aliases make the query easier to read and write.
Conditions on ticketdetails go in the on clause, not the where clause.
The condition on td.hippcode is not null is unnecessary, because NULL will not match in the join (note: you might want to check for the customer column).
case is the standard way to do the conditional sum (and hence correct). However, MySQL offers a much simpler and intuitive syntax.
Your order by was doing nothing, because it was adding two strings (hence equivalent to order by 0. Don't use single quotes ever for column names, and you won't have a problem like that.

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

Mysql group_concat with distinct and where gives strange results

I have the following query which works fine (see below).
But when I add a condition, for example AND (specialtyName = '...') the main results are fine, but the GROUP_CONCAT only shows the results that match the condition.
Can anyone please help me with this?
Thanks in advance.
Fred.
SELECT
tblJobs.jobID,
tblJobs.jobName,
DATE_FORMAT(tblJobs.jobDate,'%d-%m-%Y'),
tblCompanies.companyID,
tblCompanies.companyName,
tblCompanies.companyNameConvert,
GROUP_CONCAT(DISTINCT tblSpecialties.specialtyName
ORDER BY FIELD (
specialtyName,
'specialtyName1',
'specialtyName2',
'specialtyName3'),
specialtyName ASC)
AS specialtyNames,
GROUP_CONCAT(DISTINCT tblSpecialties.specialtyNameConvert
ORDER BY FIELD (
specialtyName,
'specialtyName1',
'specialtyName2',
'specialtyName3'),
specialtyName ASC)
AS specialtyNamesConvert,
GROUP_CONCAT(DISTINCT tblRegions.regionName),
GROUP_CONCAT(DISTINCT tblRegions.regionNameConvert)
FROM tblJobs
LEFT JOIN tblCompanies ON
(tblJobs.jobCompany = tblCompanies.companyID)
LEFT JOIN tblSpecialties ON
FIND_IN_SET(tblSpecialties.specialtyID, REPLACE(tblJobs.jobSpecialty,' ',','))
LEFT JOIN tblRegions ON
FIND_IN_SET(tblRegions.regionID, REPLACE(tblJobs.jobRegion,' ',','))
WHERE
AND jobActive = '1'
AND jobDate >= '2013-01-01'
AND companyActive = '1'
GROUP BY jobID
ORDER BY jobDate DESC, jobID DESC, jobCompany DESC
If you say:
WHERE jobActive = '1' AND jobDate >= '2013-01-01' AND companyActive = '1' AND
specialties = XXX
Then you are only going to get exactly those specialties. The filtering is done before the aggregation. As a note: including such conditions in the where clause also turns the outer joins to inner joins. Your joins are probably on properly aligned foreign key relationships, so inner joins may be appropriate.
I'm guessing what you really want is to filter jobs by those having that specialty, but to keep all other information. You want to do the filtering after the aggregation. Do this with a having clause instead of a where clause:
having sum(specialties = XXX) > 0;
This will keep only the rows that have the particular specialty, and keep all the other information.
I suppose that using aliases for your tables and subqueries could resolve your problem.
You can try something like this:
SELECT
tblJobs.jobID,
tblJobs.jobName,
DATE_FORMAT(tblJobs.jobDate,'%d-%m-%Y'),
tblCompanies.companyID,
tblCompanies.companyName,
tblCompanies.companyNameConvert,
(SELECT GROUP_CONCAT(DISTINCT ts.specialtyName
ORDER BY FIELD (
specialtyName,
'specialtyName1',
'specialtyName2',
'specialtyName3'),
specialtyName ASC)
FROM tblSpecialties ts) AS specialtyNames ,
, ... ,
FROM tblJobs
LEFT JOIN tblCompanies ON
(tblJobs.jobCompany = tblCompanies.companyID)
LEFT JOIN tblSpecialties ON
FIND_IN_SET(tblSpecialties.specialtyID, REPLACE(tblJobs.jobSpecialty,' ',','))
LEFT JOIN tblRegions ON
FIND_IN_SET(tblRegions.regionID, REPLACE(tblJobs.jobRegion,' ',','))
WHERE
AND jobActive = '1'
AND jobDate >= '2013-01-01'
AND companyActive = '1'
GROUP BY jobID
ORDER BY jobDate DESC, jobID DESC, jobCompany DESC
I didn't tested this code, but It could help.

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

Selecting blackout dates through LEFT JOIN and WHERE selector

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.

Categories