How to write following query in CakePHP 3. It has "having" clause in sub-query
the following query is working but what is the way to write CakePHP query for following complex query.
$query = "SELECT users.name, users.Id ,users.primary_image ,genders.name as genderName, FLOOR(DATEDIFF(CURDATE(),users.DOB) / 365) AS age, countries.Name as countryName, states.Name as stateName,
user_log.online_status
FROM users_address, genders , countries, states, user_log , users
WHERE gender_id = " . $looksFor->gender_id ."
AND Users_address.user_id = users.Id
AND genders.Id = users.gender_id
AND Users_address.country_id = countries.Id
AND Users_address.state_id = states.Id
AND user_log.user_id = users.Id
AND users.Id IN (
SELECT users_responses.user_id
FROM users_responses
WHERE". $conditions_list ."
GROUP BY user_id
HAVING COUNT(DISTINCT question_id, response_id) > ".$thirty_percent." );
ORDER BY RAND()
LIMIT 15 ";
}
$thirty_percent and $conditions_list have the following data which is used in sub-query to find out the results.
$thirty_percent = (int)($conditions_count/3.3 );
$conditions_list = (question_id = 3 AND response_id = 6)OR
(question_id = 5 AND response_id = 8)OR
(question_id = 12 AND response_id = 33)OR
(question_id = 6 AND response_id = 17)OR
(question_id = 9 AND response_id = 26)OR
(question_id = 7 AND response_id = 18)OR
(question_id = 1 AND response_id = 5)OR
(question_id = 8 AND response_id = 22)OR
(question_id = 2 AND response_id = 3)
EDIT:
I learnt first write sub query and then put that query in main query
Main Query: $this->Users->find('all',[
'contain'=>['Genders','Images','Users_Address', 'Countries','States'],
'conditions'=>[ 'user_id IN' => $Subquery ]
]);
Sub Query :
$Subquery = $UsersResponses->find('all',[
'conotain'=>[],
]])->where([$conditions_list, **here problem 1** ])->group('user_id')->having COUNT("(DISTINCT question_id, response_id)=>" = ".$thirty_percent." )here problem 2 ;
SQL sub-Query
SELECT users_responses.user_id
FROM users_responses
WHERE". $conditions_list ."
GROUP BY user_id
HAVING COUNT(DISTINCT question_id, response_id) > ".$thirty_percent." );
ORDER BY RAND()
LIMIT 15 ";
As you see in sub-query I need to put $conditions_list which have arrays of conditions and how we can put here in CAKEPHP.
problem 2 :need to use
GROUP BY user_id
HAVING COUNT(DISTINCT question_id, response_id) > ".$thirty_percent." );
ORDER BY RAND()
LIMIT 15 "; in sub-query but there is no clue how to do it.
Related
I need to select all chats and right now I'm selecting them using multiple queries like this:
$query_1 = mysqli_query($database, "SELECT chat_id, user_id_1, user_id_2
FROM chat
WHERE user_id_1 = '$user_id' OR user_id_2 = '$user_id')");
if (mysqli_num_rows($query_1) > 0) {
while ($row = mysqli_fetch_assoc($query_1)) {
$chat_id = $row['chat_id'];
$user_id_1 = $row['user_id_1'];
$user_id_2 = $row['user_id_2'];
if ($user_id_1 == $user_id)
$user_id_chat = $user_id_2;
else
$user_id_chat = $user_id_1;
$query_2 = mysqli_query($database, "SELECT username
FROM user
WHERE user_id = '$user_id_chat'");
$row2 = mysqli_fetch_assoc($query_2);
$username = $row2['username'];
$query_3 = mysqli_query($database, "SELECT text,
(SELECT COUNT(message_id) FROM message WHERE chat_id = '$chat_id' AND user_id != '$user_id' AND seen = '0') AS unread_messages
FROM message
WHERE chat_id = '$chat_id'
ORDER BY message_id DESC
LIMIT 1");
$row3 = mysqli_fetch_assoc($query_3);
$text = $row3['text'];
$unread_messages = $row3['unread_messages'];
}
}
Is there a way to get all this ($chat_id, $user_id_chat, $username, $text and $unread_messages) using only one query?
EDIT: Added example data and expected output:
Table user
user_id username ...
1 User1 ...
2 User2 ...
3 User3 ...
4 User4 ...
Table chat
chat_id user_id_1 user_id_2 ...
1 3 4 ...
2 1 3 ...
Table message
message_id chat_id user_id text seen ...
1 1 4 Hi! 1 ...
2 2 1 Hello 1 ...
3 1 3 hi 0 ...
4 2 3 Hi 0 ...
5 2 3 How are you? 0 ...
If $user_id is 1 there should be only one chat and value of $chat_id should be 2, of $user_id_chat should be 3, of $username should be 'User3', of $text should be 'How are you?' and of $unread_messages should be 2.
http://sqlfiddle.com/#!9/8c6cb/8
$query_1 = mysqli_query($database,
"SELECT
c.chat_id,
u.username,
COALESCE(m.text,'MY PLACEHOLDER'),
COALESCE(um.unread_messages,0)
FROM (
SELECT chat_id,
IF(user_id_1 = '$user_id', user_id_2, user_id_1) user_id_chat
FROM chat
WHERE user_id_1 = '$user_id' OR user_id_2 = '$user_id'
) c
LEFT JOIN user u
ON u.user_id = c.user_id_chat
LEFT JOIN (SELECT chat_id, SUM(seen = '0',1,0) unread_messages, MAX(message_id) max_id
FROM message
WHERE user_id != '$user_id'
GROUP BY chat_id) um
ON um.chat_id = c.chat_id
LEFT JOIN message m
ON m.chat_id = c.chat_id
AND m.message_id = um.max_id ");
if (mysqli_num_rows($query_1) > 0) {
while ($row = mysqli_fetch_assoc($query_1)) {
$chat_id = $row['chat_id'];
$username = $row['username'];
$text = $row ['text'];
$unread_messages = $row['unread_messages'];
}
}
So, you are looking for a way to select from two tables in one query. Then you might like INNER JOIN.
Visit this link:
http://www.w3schools.com/sql/sql_join_inner.asp
OR
Make a function to select like this:
` function select ($query ="SELECT * FROM $table", $limit = 1) {
$row = mysql_query($query);
// I'm so lazy to type the other codes, just put ur while loop and other stuff(if any) here
} `
This then must be called as:
` select("SELECT chat_id, user_id_1, user_id_2
FROM chat
WHERE user_id_1 = '$user_id' OR user_id_2 = '$user_id')");
select(" (SELECT COUNT(message_id) FROM message WHERE chat_id = '$chat_id' AND user_id != '$user_id' AND seen = '0') AS unread_messages
FROM message
WHERE chat_id = '$chat_id' AND user_id != '$user_id'
ORDER BY message_id DESC
LIMIT 1"); `
This won't need multiple where loops.
Sorry for bad code layout, I'm a newbie here.
Good Luck!
Is there a way to join multiple count(*) queries to a single one? I know that i don't need to prepare the last two queries two times but that's not what the question is about. I have no idea how to do this in a correct way.
$sth = $this->_db->prepare('SELECT count(*) FROM posts WHERE topicid = ?');
$sth->execute( array( $this->_id ) );
$numPosts = $sth->fetch(\PDO::FETCH_ASSOC);
$sth = $this->_db->prepare('SELECT count(*) FROM topic_activity WHERE topicid = ?');
$sth->execute( array( $this->_id ) );
$numViews = $sth->fetch(\PDO::FETCH_ASSOC);
$sth = $this->_db->prepare('SELECT count(*) FROM topic_activity WHERE topicid = ? AND likes = 1');
$sth->execute( array( $this->_id ) );
$numLikes = $sth->fetch(\PDO::FETCH_ASSOC);
$sth = $this->_db->prepare('SELECT count(*) FROM topic_activity WHERE topicid = ? AND likes = -1');
$sth->execute( array( $this->_id ) );
$numDislikes = $sth->fetch(\PDO::FETCH_ASSOC);
SELECT count_post, topic_activity
FROM(
SELECT count(*) AS count_post, NULL AS topic_activity FROM posts WHERE topicid = ?
UNION
SELECT NULL AS count_post, count(*) AS topic_activity FROM topic_activity WHERE topicic =?
) T
GROUP by count_post, topic_activity
let me know if it works
Although the answer of user3106988 'works', I would have gone for a more 'vertical' approach (and also store the id in a variable so you only need to pass it once, could be this doesn't work in your environment though, so could be you'll need to replace the #id's with 4 question marks, but hopefully not) :
SET #id = ?;
SELECT info = 'Posts', count(*) FROM posts WHERE topicid = #id
UNION ALL
SELECT info = 'Views', count(*) FROM topic_activity WHERE topicid = #id
UNION ALL
SELECT info = 'Likes', count(*) FROM topic_activity WHERE topicid = #id AND likes = 1
UNION ALL
SELECT info = 'DisLikes', count(*) FROM topic_activity WHERE topicid = #id AND likes = -1
But that returns 4 records where you'll then need to extract the data out by means of the info field. So, building on the idea of user3106988's answer to return just 1 record with all the info in different fields you could actually go for the likes & disklikes in as little effort as below. Should be quite fast.
SET #id = ?;
SELECT SUM(count_posts) as count_posts,
SUM(views) as views,
SUM(likes) as likes,
SUM(dislikes) as dislikes
FROM(
SELECT COUNT(*) as count_posts,
0 as views,
0 as likes,
0 as dislikes
FROM posts
WHERE topicid = #id
UNION ALL
SELECT 0 as count_posts,
COUNT(*) as views,
SUM(Case WHEN likes = 1 THEN 1 ELSE 0 END) as likes,
SUM(Case WHEN likes = -1 THEN 1 ELSE 0 END) as dislikes
FROM topic_activity
WHERE topicid = #id
) T;
I currently have the following queries (and some surrounding vB code). I was hoping I could slim this up into a single SELECT statement versus having to run 10 more on each page to get that names of individuals.
$results = $db->query_read_slave("
SELECT user.id AS steam, user.skills AS level
FROM wcsp.wcgousers AS user
WHERE race = '_wcs_'
ORDER BY ABS(user.skills) DESC LIMIT 10
");
$rank = 1;
while ($user = $db->fetch_array($results)) {
$result = $db->query_first("
SELECT old.name AS name
FROM wcsp.warn_oldnames AS old
WHERE id = '$user[steam]'
ORDER BY lasttime
DESC LIMIT 1
");
$listing[] = array("id" => $user['steam'], "level" => $user['level'], "name" => $result['name'], "rank" => $rank);
$rank += 1;
}
I have tried LEFT JOIN but the issue I run into is that I would need a subquery in the LEFT JOIN similar to:
SELECT user.id AS steam, user.skills AS level, names.name AS name
FROM wcsp.wcgousers AS users
LEFT JOIN
(
SELECT names.name
FROM wcsp.warn_oldnames AS names
WHERE id = wcsp.wcgousers.id
ORDER BY lasttime DESC LIMIT 1
) AS names
ON names.id = users.id
WHERE users.race = '_wcs_'
Which won't work due to the different database check inside the subquery.
If I understand you correctly, you want to get the latest Name for every users.
SELECT a.id AS steam,
a.skills AS level,
b.name
FROM wcgousers a
INNER JOIN warn_oldnames b
ON a.ID = b.ID
INNER JOIN
(
SELECT ID, MAX(lasttime) max_date
FROM warn_oldnames
GROUP BY ID
) c ON b.ID = c.ID AND
b.lastTime = c.max_date
WHERE a.race = '_wcs_'
-- ORDER BY ABS(a.skills) DESC
-- LIMIT 10
$sql = 'SELECT have_show FROM `date` WHERE DATE(`date`.day) = ?';
$this->db->query($sql, array($date)); //works fine
This works fine as expected.
$sql = 'SELECT actors, visitors, tickets, have_show FROM show
JOIN `date` ON show.day = `date`.id
WHERE DATE(`date`.day) = ?';
$this->db->query($sql, array($date));
here, with joined statements it gives 0 results.
db schema
id(AI) day(INT FK-date.id) actors(INT) visitors(INT)
1 2 3 45
id(AI) day(DATE) have_show(BOOLEAN)
1 2012-12-06 0
2 2012-12-07 1
codeigniter's active records does the same.
Any of you ever suffered from this kind of poblems?
try this one
$sql = 'SELECT actors, visitors, tickets, acts FROM `show`
JOIN `date` ON show.day = date.id
WHERE DATE(date.day) = ?';
$this->db->query($sql, array($date));
you have mixed between date.day and date.id.
or maybe u have show.id not show.day so look what u have in your table and if yes
replace in your sql show.day by show.id
EDIT:
$sql = 'SELECT s.day s.actors, s.visitors, s.tickets, s.acts d.day FROM `show` s
JOIN `date` d ON s.day = d.id ';
I have the following query (Wordpress DB).
This will return data for the comment that has the most combined "up" and "down" ratings:
$comment_query = $wpdb->get_results("
SELECT wp_comments.*, wp_comment_rating.*, (wp_comment_rating.ck_rating_up+wp_comment_rating.ck_rating_down) AS pop_comment
FROM wp_comments, wp_comment_rating
WHERE wp_comments.comment_post_ID = $post->ID
AND wp_comments.comment_ID = wp_comment_rating.ck_comment_id
AND wp_comments.comment_approved = 1
ORDER BY pop_comment
DESC
LIMIT 1");
However, I'd also like to factor in comments that have the most replies by counting the number of matched "comment_parent" per comment, then adding that total to the "pop_comment" value I'm ordering by.
Essentially, I want to get the data for a comment with the most combined replies and up/down ratings.
Hope that makes sense...
You could subquery to get the counts by comment_parent
$comment_query = $wpdb->get_results("
SELECT wp_comments.*, wp_comment_rating.*, (wp_comment_rating.ck_rating_up+wp_comment_rating.ck_rating_down + (
select count(*) from wp_comments c2 where c2.comment_parent=wp_comments.comment_ID
)) AS pop_comment
FROM wp_comments, wp_comment_rating
WHERE wp_comments.comment_post_ID = $post->ID
AND wp_comments.comment_ID = wp_comment_rating.ck_comment_id
AND wp_comments.comment_approved = 1
ORDER BY pop_comment
DESC
LIMIT 1");
This should work
Essentially its just a sub query to get the total comments for the comment_parent and then joins that as SubQ / SubCount to the field
comment_query = $wpdb->get_results("
SELECT wp_comments.*, wp_comment_rating.*, (SubCount + pop_comment) AS total_rank, (wp_comment_rating.ck_rating_up+wp_comment_rating.ck_rating_down) AS pop_comment
JOIN (SELECT COUNT(*) as SubCount, comment_parent as Sub_ID FROM wp_comments GROUP BY comment_parent) as SubQ
ON wp_comments.comment_ID = SubQ.Sub_ID
FROM wp_comments, wp_comment_rating
WHERE wp_comments.comment_post_ID = $post->ID
AND wp_comments.comment_ID = wp_comment_rating.ck_comment_id
AND wp_comments.comment_approved = 1 ORDER BY total_rank DESC LIMIT 1");