There are two tables over here: first is for basic details of "poll" and
second for the "answer". My requirement is like this:
i want poll records to display only for users who didn't answer the poll
I tried this query but it is not working:
select p.* from mycom_poll as p
LEFT JOIN mycom_polls_result as pr on (p.b_id != pr.b_poll_id
And pr.b_user_id !=14) where p.v_status = 'enable'
And p.v_country like '%India%'
AND p.d_activate_date <= '2015-04-17' order by p.b_id limit 1"
CREATE TABLE IF NOT EXISTS `mycom_poll` (
`b_id` bigint(20) NOT NULL AUTO_INCREMENT,
`b_user_id` bigint(20) NOT NULL DEFAULT '0',
`v_code` varchar(50) NOT NULL,
`v_question` varchar(1000) CHARACTER SET utf8 NOT NULL,
`v_country` varchar(100) NOT NULL,
`d_activate_date` date NOT NULL,
`dt_created_date` datetime NOT NULL,
`dt_updated_date` datetime NOT NULL,
`v_ip` varchar(20) NOT NULL,
`v_status` varchar(20) NOT NULL DEFAULT 'enable',
PRIMARY KEY (`b_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;
INSERT INTO `mycom_poll` (`b_id`, `b_user_id`, `v_code`, `v_question`, `v_country`, `d_activate_date`, `dt_created_date`, `dt_updated_date`, `v_ip`, `v_status`) VALUES(8, 0, '20150417115406-192168113C5ZJpXu2hM', 'Who is chief minstor of Gujarat in 2015 Rajyasabha?', '["Global","India","United States"]', '2015-04-15', '2015-04-17 11:56:12', '2015-04-18 07:29:11', '127.0.0.1', 'enable');
CREATE TABLE IF NOT EXISTS `mycom_polls_result` (
`b_id` bigint(20) NOT NULL AUTO_INCREMENT,
`b_poll_id` bigint(20) NOT NULL,
`b_user_id` bigint(20) NOT NULL,
`b_poll_answer` bigint(20) NOT NULL,
`dt_created_date` datetime NOT NULL,
`dt_updated_date` datetime NOT NULL,
`v_ip` varchar(20) NOT NULL,
`v_status` varchar(20) NOT NULL DEFAULT 'enable',
PRIMARY KEY (`b_id`),
KEY `b_poll_id` (`b_poll_id`,`b_user_id`),
KEY `b_poll_id_2` (`b_poll_id`,`b_user_id`,`b_poll_answer`),
KEY `b_poll_id_3` (`b_poll_id`,`b_user_id`,`b_poll_answer`),
KEY `b_user_id` (`b_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `mycom_polls_result` (`b_id`, `b_poll_id`, `b_user_id`, `b_poll_answer`, `dt_created_date`, `dt_updated_date`, `v_ip`, `v_status`) VALUES
(1, 8, 14, 64, '2015-04-18 13:27:50', '2015-04-18 13:27:50', '127.0.0.1', 'enable');
Your query:
select p.* from mycom_poll as p
LEFT JOIN mycom_polls_result as pr on (p.b_id != pr.b_poll_id
And pr.b_user_id !=14) where p.v_status = 'enable'
And p.v_country like '%India%'
AND p.d_activate_date <= '2015-04-17' order by p.b_id limit 1
First I am going to point out the mistake of this query, what you are doing here is, you are left joining two tables on id of first table not equals to id of second table which will join all the table data whose ids are not same, when you do a left join, you always get the data of left table and if data not present in right table you get null.
So in where clause you should check any if the field of second table is null or not like this.
select p.* from mycom_poll as p
LEFT JOIN mycom_polls_result as pr on p.b_id = pr.b_poll_id
where pr.b_user_id !=14 p.v_status = 'enable'
And p.v_country like '%India%' AND p.d_activate_date <= '2015-04-17'
and pr.b_poll_id is null order by p.b_id limit 1
I request you to understand left join first.
At the moment you are using LEFT JOIN which is correct to force all rows of the mycom_poll table to be considered, but you're joining on p.b_id != pr.b_poll_id which will associate each poll row in mycom_poll with all the unrelated results rows in mycom_polls_result that don't belong to it.
You probably need to join on p.b_id = pr.b_poll_id, and then require rows with pr.b_poll_id IS NULL in your WHERE clause.
You also seem to be trying to restrict results to pr.b_user_id !=14, which is fine, but that needs to be stated along with other filtering conditions in the WHERE clause. The LEFT JOIN ON clause is just going to be used to declare the column relationships between your tables.
SELECT p.*
FROM mycom_poll as p
LEFT JOIN mycom_polls_result as pr
ON p.b_id = pr.b_poll_id
WHERE
pr.b_user_id !=14
AND p.v_status = 'enable'
AND p.v_country like '%India%'
AND p.d_activate_date <= '2015-04-17'
AND pr.b_poll_id IS NULL
ORDER BY p.b_id LIMIT 1
HTH
Related
Working on a support ticketing system with not a lot of tickets (~3,000). To get a summary grid of ticket information, there are five LEFT JOIN statements on custom field table (j25_field_value) containing about 10,000 records. The query runs too long (~10 seconds) and in cases with a WHERE clause, it runs even longer (up to ~30 seconds or more).
Any suggestions for improving the query to reduce the time to run?
Four tables:
j25_support_tickets
CREATE TABLE `j25_support_tickets` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`category_id` int(11) NOT NULL DEFAULT '0',
`user_id` int(11) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
`subject` varchar(255) DEFAULT NULL,
`message` text,
`modified_date` datetime DEFAULT NULL,
`priority_id` tinyint(3) unsigned DEFAULT NULL,
`status_id` tinyint(3) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3868 DEFAULT CHARSET=utf8
j25_support_priorities
CREATE TABLE `j25_support_priorities` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=14 DEFAULT CHARSET=utf8
j25_support_statuses
CREATE TABLE `j25_support_statuses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
j25_field_value (id, ticket_id, field_id, field_value)
CREATE TABLE `j25_support_field_value` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`ticket_id` int(11) DEFAULT NULL,
`field_id` int(11) DEFAULT NULL,
`field_value` tinytext,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=10889 DEFAULT CHARSET=utf8
Also, ran this:
SELECT LENGTH(field_value) len FROM j25_support_field_value ORDER BY len DESC LIMIT 1
note: the result = 38
The query:
SELECT DISTINCT t.id as ID
, (select p.title from j25_support_priorities p where p.id = t.priority_id) as Priority
, (select s.title from j25_support_statuses s where s.id = t.status_id) as Status
, t.subject as Subject
, t.email as SubmittedByEmail
, type.field_value AS IssueType
, ver.field_value AS Version
, utype.field_value AS UserType
, cust.field_value AS Company
, refno.field_value AS RefNo
, t.modified_date as Modified
FROM j25_support_tickets AS t
LEFT JOIN j25_support_field_value AS type ON t.id = type.ticket_id AND type.field_id =1
LEFT JOIN j25_support_field_value AS ver ON t.id = ver.ticket_id AND ver.field_id =2
LEFT JOIN j25_support_field_value AS utype ON t.id = utype.ticket_id AND utype.field_id =3
LEFT JOIN j25_support_field_value AS cust ON t.id = cust.ticket_id AND cust.field_id =4
LEFT JOIN j25_support_field_value AS refno ON t.id = refno.ticket_id AND refno.field_id =5
ALTER TABLE j25_support_field_value
ADD INDEX (`ticket_id`,`field_id`,`field_value`(50))
This index will work as a covering index for your query. It will allow the joins to use only this index to look up the values. It should perform massively faster than without this index, since currently your query would have to read every row in the table to find what matches each combination of ticket_id and field_id.
I would also suggest converting your tables to InnoDB engine, unless you have a very explicit reason for using MyISAM.
ALTER TABLE tablename ENGINE=InnoDB
As above - a better index would help. You could probably then simplify your query into something like this too (join to the table only once):
SELECT t.id as ID
, p.title as Priority
, s.title as Status
, t.subject as Subject
, t.email as SubmittedByEmail
, case when v.field_id=1 then v.field_value else null end as IssueType
, case when v.field_id=2 then v.field_value else null end as Version
, case when v.field_id=3 then v.field_value else null end as UserType
, case when v.field_id=4 then v.field_value else null end as Company
, case when v.field_id=5 then v.field_value else null end as RefNo
, t.modified_date as Modified
FROM j25_support_tickets AS t
LEFT JOIN j25_support_field_value v ON t.id = v.ticket_id
LEFT JOIN j25_support_priorities p ON p.id = t.priority_id
LEFT JOIN j25_support_statuses s ON s.id = t.status_id;
You can do away with the subqueries for starters and just get them from another join. You can add an index to j25_support_field_value
alter table j25_support_field_value add key(id, field_type);
I assume there is an index on id in j25_support_tickets - if not and if they are unique, add a unique index alter table j25_support_tickets add unique key(id); If they're not unique, remove the word unique from that statement.
In MySQL, a join usually requires an index on the field(s) that you are using to join on. This will hold up and produce very reasonable results with huge tables (100m+), if you follow that rule, you will not go wrong.
are the ids in j25_support_tickets unique? If they are you can do away with the distinct - if not, or if you are getting exact dupicates in each row, still do away with the distinct and add a group by t.id to the end of this:
SELECT t.id as ID
, p.title as Priority
, s.title as Status
, t.subject as Subject
, t.email as SubmittedByEmail
, type.field_value AS IssueType
, ver.field_value AS Version
, utype.field_value AS UserType
, cust.field_value AS Company
, refno.field_value AS RefNo
, t.modified_date as Modified
FROM j25_support_tickets AS t
LEFT JOIN j25_support_field_value AS type ON t.id = type.ticket_id AND type.field_id =1
LEFT JOIN j25_support_field_value AS ver ON t.id = ver.ticket_id AND ver.field_id =2
LEFT JOIN j25_support_field_value AS utype ON t.id = utype.ticket_id AND utype.field_id =3
LEFT JOIN j25_support_field_value AS cust ON t.id = cust.ticket_id AND cust.field_id =4
LEFT JOIN j25_support_field_value AS refno ON t.id = refno.ticket_id AND refno.field_id =5
LEFT JOIN j25_support_priorities p ON p.id = t.priority_id
LEFT JOIN j25_support_statuses s ON s.id = t.status_id;
Switch to InnoDB.
After switching to InnoDB, make the PRIMARY KEY for j25_support_field_value be (ticket_id, field_id) (and get rid if id). (Tacking on field_value(50) will hurt, not help.)
A PRIMARY KEY is a UNIQUE KEY, so don't have both.
Use VARCHAR(255) instead of the nearly-equivalent TINYTEXT.
EAV schema sucks. My ran on EAV.
I have this query:
select
b.user as user1, b.timestamp as ts1,
c.user as user2, c.timestamp as ts2,
d.user as user3, d.timestamp as ts3,
e.user as user4, e.timestamp as ts4,
f.user as user5, f.timestamp as ts5,
g.user as user6, g.timestamp as ts6,
h.user as user7, h.timestamp as ts7,
i.user as user8, i.timestamp as ts8,
j.user as user9, j.timestamp as ts9,
k.user as user10, k.timestamp as ts10,
a.beschreibung, a.auftragsnummer, a.faellig, a.subkunde,
(SELECT firma from kunden where id=a.kunde limit 0,1) as kunde,
(SELECT kommision from kommision where id=a.kommision limit 0,1) as kommision
from auftrag a
left join details b on (b.beschreibung='Step1' AND b.auftrags_id=a.id)
left join details c on (c.beschreibung='Step2' AND c.auftrags_id=a.id)
left join details d on (d.beschreibung='Step3' AND d.auftrags_id=a.id)
left join details e on (e.beschreibung='Step4' AND e.auftrags_id=a.id)
left join details f on (f.beschreibung='Step5' AND f.auftrags_id=a.id)
left join details g on (g.beschreibung='Step6' AND g.auftrags_id=a.id)
left join details h on (h.beschreibung='Step7' AND h.auftrags_id=a.id)
left join details i on (i.beschreibung='Step8' AND i.auftrags_id=a.id)
left join details j on (j.beschreibung='Step9' AND j.auftrags_id=a.id)
left join details k on (k.beschreibung='Step10' AND k.auftrags_id=a.id)
where a.erledigt='1'
It runs very, very slow and take about 1 Minute to get a resultset.
Table "auftrag" has 820 rows and table "details" about 7000 rows.
What am I doing wrong?
Thanks
Patrick
CREATE TABLE auftrag
(
id INT(10) NOT NULL AUTO_INCREMENT,
beschreibung VARCHAR(255) NULL DEFAULT '0',
auftragsnummer VARCHAR(50) NULL DEFAULT '0',
faellig VARCHAR(50) NULL DEFAULT NULL,
kunde INT(11) NULL DEFAULT NULL,
subkunde VARCHAR(50) NULL DEFAULT NULL,
kommision VARCHAR(50) NULL DEFAULT NULL,
notiz TEXT NULL,
werbeanbringung TEXT NULL,
erledigt INT(1) NULL DEFAULT NULL,
INDEX Schlüssel 1 (id),
INDEX Schlüssel 2 (id, auftragsnummer, kunde, subkunde, beschreibung)
) COLLATE='latin1_swedish_ci' ENGINE=MyISAM AUTO_INCREMENT=850 ;
CREATE TABLE details
(
id INT(11) NOT NULL AUTO_INCREMENT,
auftrags_id VARCHAR(50) NULL DEFAULT '0',
beschreibung VARCHAR(50) NULL DEFAULT '0',
user VARCHAR(50) NULL DEFAULT '0',
timestamp VARCHAR(50) NULL DEFAULT '0',
notiz VARCHAR(255) NULL DEFAULT NULL,
INDEX Schlüssel 2 (user, timestamp, beschreibung, auftrags_id),
INDEX Schlüssel 1 (id, user, timestamp, beschreibung)
) COLLATE='latin1_swedish_ci' ENGINE=MyISAM AUTO_INCREMENT=7260 ;
You have no indexes on your tables that are useful for this query, and you are joining one table against another repeatedly.
On the auftrag table add an index on the erledigt column.
On the details table add an index covering both the beschreibungand the auftrags_id columns
It may also be possible to avoid most of the joins if you are prepared to process the returned data afterwards (ie, split up a returned field into an array). But a bit messy:-
SELECT
GROUP_CONCAT(CONCAT_WS('##', b.beschreibung, b.user, b.timestamp)),
a.beschreibung, a.auftragsnummer, a.faellig, a.subkunde,
(SELECT firma from kunden where id=a.kunde limit 0,1) as kunde,
(SELECT kommision from kommision where id=a.kommision limit 0,1) as kommision
FROM auftrag a
LEFT OUTER JOIN details b on b.auftrags_id=a.id AND b.beschreibung IN ('Step1', 'Step2', 'Step3', 'Step4', 'Step5', 'Step6', 'Step7', 'Step8', 'Step9', 'Step10')
WHERE a.erledigt='1'
GROUP BY a.id
EDIT - I have just noticed that the id on the auftrag table is an INT field (as I would expect) but the auftrags_id column on the details table is a VARCHAR(50) field. This will dramatically affect performance as it needs to convert the values for every single comparison.
Note that Gordons comment above on the order of the fields on the index does apply (how much will depend on the actual data - for example how many other values of beschreibung there are), but without fixing this problem with the mismatched data types changing the index will almost certainly not help.
For your query, you want the following indexes:
auftrag(erledigt, id)
details(auftrags_id, beschreibung)
kunden(kunde, firma)
You can also rewrite the query as a conditional aggregation, but the right indexes will be a big help.
Note: the order of the columns in the composite indexes is important.
This is probably a case where InnoDB will be a clear winner over MyISAM. In details, have PRIMARY KEY(auftrags_id, beschreibung).
Meanwhile, in auftrag, PRIMARY KEY(id). And the indexes Gordon suggests for auftrag and kunden.
I have this query for example (good, it works how I want it to)
SELECT `discusComments`.`memberID`, COUNT( `discusComments`.`memberID`) AS postcount
FROM `discusComments`
GROUP BY `discusComments`.`memberID` ORDER BY postcount DESC
Example Results:
memberid postcount
3 283
6 230
9 198
Now I want to join the memberid of the discusComments table with that of the discusTopic table (because what I really want to do is only get my results from a specific GROUP, and the group id is only in the topic table and not in the comment one hence the join.
SELECT `discusComments`.`memberID`, COUNT( `discusComments`.`memberID`) AS postcount
FROM `discusComments`
LEFT JOIN `discusTopics` ON `discusComments`.`memberID` = `discusTopics`.`memberID`
GROUP BY `discusComments`.`memberID` ORDER BY postcount DESC
Example Results:
memberid postcount
3 14789
6 8678
9 6987
How can I stop this huge increase happening in the postcount? I need to preserve it as before.
Once I have this sorted I want to have some kind of line which says WHERE discusTopics.groupID = 6, for example
CREATE TABLE IF NOT EXISTS `discusComments` (
`id` bigint(255) NOT NULL auto_increment,
`topicID` bigint(255) NOT NULL,
`comment` text NOT NULL,
`timeStamp` bigint(12) NOT NULL,
`memberID` bigint(255) NOT NULL,
`thumbsUp` int(15) NOT NULL default '0',
`thumbsDown` int(15) NOT NULL default '0',
`status` int(1) NOT NULL default '1',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=7190 ;
.
CREATE TABLE IF NOT EXISTS `discusTopics` (
`id` bigint(255) NOT NULL auto_increment,
`groupID` bigint(255) NOT NULL,
`memberID` bigint(255) NOT NULL,
`name` varchar(255) NOT NULL,
`views` bigint(255) NOT NULL default '0',
`lastUpdated` bigint(10) NOT NULL,
PRIMARY KEY (`id`),
KEY `groupID` (`groupID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=913 ;
SELECT `discusComments`.`memberID`, COUNT( `discusComments`.`memberID`) AS postcount
FROM `discusComments`
JOIN `discusTopics` ON `discusComments`.`topicID` = `discusTopics`.`id`
GROUP BY `discusComments`.`memberID` ORDER BY postcount DESC
Joining the topicid in both tables solved the memberID issue. Thanks #Andiry M
You need to use just JOIN not LEFT JOIN and you can add AND discusTopics.memberID = 6 after ON discusComments.memberID = discusTopics.memberID
You can use subqueries lik this
SELECT `discusComments`.`memberID`, COUNT( `discusComments`.`memberID`) AS postcount
FROM `discusComments` where `discusComments`.`memberID` in
(select distinct memberid from `discusTopics` WHERE GROUPID = 6)
If i understand your question right you do not need to use JOIN here at all. JOINs are needed in case when you have many to many relationships and you need for each value in one table select all corresponding values in another table.
But here you have many to one relationship if i got it right. Then you can simply do select from two tables like this
SELECT a.*, b.id FROM a, b WHERE a.pid = b.id
This is simple request and won't create a giant overhead as JOIN does
PS: In the future try to experiment with your queries, try to avoid JOINs especially in MySQL. They are slow and dangerous in their complexity. For 90% of cases when you want to use JOIN there is simple and much faster solution.
I have been trying to select data from multiple tables whereas I sort by the count of id's from an additional table that I have joined with a Left Join. Everything works great except for that the count gets incremented with 600something instead of one for each row.
I tried as I saw in a similar problem to put distinct inside the count statement but with the only result of freezing the database.
Tables:
CREATE TABLE IF NOT EXISTS `places` (
`PlaceId` int(10) NOT NULL AUTO_INCREMENT,
`Name` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
`AreaId` int(10) NOT NULL DEFAULT '1',
PRIMARY KEY (`PlaceId`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=0;
CREATE TABLE IF NOT EXISTS `places_descriptions` (
`DescId` int(45) NOT NULL AUTO_INCREMENT,
`PlaceId` int(10) NOT NULL,
`Description` varchar(1024) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`DescId`),
KEY `PlaceId` (`PlaceId`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=0;
CREATE TABLE IF NOT EXISTS `places_hits` (
`HitId` int(45) NOT NULL AUTO_INCREMENT,
`PlaceId` int(45) NOT NULL,
PRIMARY KEY (`HitId`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=0;
Query:
SELECT
p.PlaceId,
p.Name,
pd.Description,
COUNT(ph.HitId) AS numHits
FROM
places_descriptions AS pd,
places AS p
LEFT JOIN places_hits AS ph
ON (p.PlaceId = ph.PlaceId)
WHERE
(p.PlaceId = pd.PlaceId) AND
(p.AreaId = 1)
GROUP BY
p.PlaceId,
p.Name,
pd.Description
ORDER BY
numHits DESC,
p.PlaceId
LIMIT 0, 10
Any ideas? Thank you!
At first glance, your problem may be that you're cross-joining pd and p because there's no ON clause. The placeId=placeId in the where masks this problem by filtering out the rows where the join was inaccurate, but it'd be better to place that condition in the ON. The large number of rows actually existing before filtering might be throwing off the count, though I'm not sure.
Try this, to start:
SELECT
p.PlaceId,
p.Name,
pd.Description,
COUNT(ph.HitId) AS numHits
FROM
places_descriptions AS pd
INNER JOIN places AS p
ON p.PlaceId = pd.PlaceID
LEFT JOIN places_hits AS ph
ON (p.PlaceId = ph.PlaceId)
WHERE
(p.AreaId = 1)
GROUP BY
p.PlaceId,
p.Name,
pd.Description
ORDER BY
numHits DESC,
p.PlaceId
I need some help with a MySQL query. I have two tables, one with offers and one with statuses. An offer can has one or more statuses. What I would like to do is get all the offers and their latest status. For each status there's a table field named 'added' which can be used for sorting.
I know this can be easily done with two queries, but I need to make it with only one because I also have to apply some filters later in the project.
Here's my setup:
CREATE TABLE `test`.`offers` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`client` TEXT NOT NULL ,
`products` TEXT NOT NULL ,
`contact` TEXT NOT NULL
) ENGINE = MYISAM ;
CREATE TABLE `statuses` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`offer_id` int(11) NOT NULL,
`options` text NOT NULL,
`deadline` date NOT NULL,
`added` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
Should work but not very optimal imho :
SELECT *
FROM offers
INNER JOIN statuses ON (statuses.offer_id = offers.id
AND statuses.id =
(SELECT allStatuses.id
FROM statuses allStatuses
WHERE allStatuses.offer_id = offers.id
ORDER BY allStatuses.added DESC LIMIT 1))
Try this:
SELECT
o.*
FROM offers o
INNER JOIN statuses s ON o.id = s.offer_id
ORDER BY s.added
LIMIT 1