Using mysql "IN" operator in this kind of query - php

I have the query below that uses union.
"SELECT * FROM (
SELECT 1 AS `table`,
`comment_post_id` AS `feed_id`,
`blog_id` AS `from_blog`,
`comment_author` AS `author`,
`comment_content_stripped` AS `feed_title`,
`comment_content` AS `post_content_s`,
`type` AS `type`,
null AS `object_type`,
`comment_date_gmt` AS `date`
FROM `wp_site_comments`
UNION
SELECT 2 AS `table`,
`post_id` AS `feed_id`,
null AS `from_blog`,
`blog_id` AS `author`,
`post_title` AS `feed_title`,
`post_content_stripped` AS `post_content_s`,
`post_type` AS `type`,
null AS `object_type`,
`post_published_gmt` AS `date`
FROM `wp_site_posts`
UNION
SELECT 3 AS `table`,
`object_id` AS `feed_id`,
`blog_id` AS `from_blog`,
`user_id` AS `author`,
null AS `feed_title`,
null AS `post_content_s`,
`type` AS `type`,
`object_type` AS `object_type`,
`date_added` AS `date`
FROM `wp_global_likes`
UNION
SELECT 4 AS `table`,
`object_id` AS `feed_id`,
null AS `from_blog`,
`user_id` AS `author`,
null AS `feed_title`,
null AS `post_content_s`,
`type` AS `type`,
`object_type` AS `object_type`,
`date_added` AS `date`
FROM `wp_global_followers`
) AS tb
ORDER BY `date` DESC"
Basically I wanted to select only the rows where author is in a comma separated values as follows:
eg. $blog_ids = (23, 55, 19, 10) and $user_ids = (22, 55, 19, 40)
The first table in union, the author is comment_author which is a user id.
The second table in union, the author isblog_id` which is a blog id.
The third and fourth table in union, the author is user_id which is a user_id.
Now, I wanted to somehow distinct the author as blog id and user id so my query will select rows where author is in $blog_ids and $user_ids uniquely.
I used,
WHERE author in (" . $blog_ids . ") and it returns correct. Now I wanted to include $user_id.
Please note that $blog_ids and $user_ids may have a same value.
I hope you get what I mean, this is i guess the best explanation I can make.

Related

PHP MysqlI - How to Select 1 Row? (from select)

I am a bit stumped on how i should be able to Define 1 post ID, and only select from that row
my query:
SELECT
posts.post_id,
posts.title,
COUNT(post_likes.id) AS likes,
GROUP_CONCAT(user.name SEPARATOR '|') AS liked
FROM
posts
LEFT JOIN post_likes ON post_likes.post_id = posts.post_id
LEFT JOIN user ON post_likes.user = user.id
GROUP BY
posts.post_id
my end goal is to get all from row 3 (only row 3), not get all rows. when i run the query above, i get every row from mysql.
example:
$query = "
SELECT
posts.post_id,
posts.title,
COUNT(post_likes.id) AS likes,
GROUP_CONCAT(user.name SEPARATOR '|') AS liked
FROM
posts
LEFT JOIN post_likes ON post_likes.post_id = posts.post_id
LEFT JOIN user ON post_likes.user = user.id
GROUP BY
posts.post_id
";
$result = mysqli_query($connect, $query);
while($row = mysqli_fetch_array($result))
{
echo '<h3>'.$row["title"].'</h3>';
echo 'Like';
echo '<p>'.$row["likes"].' People like this</p>';
if(count($row["liked"]))
{
$liked = explode("|", $row["liked"]);
echo '<ul>';
foreach($liked as $like)
{
echo '<li>'.$like.'</li>';
}
echo '</ul>';
}
}
returns with all rows,
what i need to do is just return the 3rd row (from the post_id, and only that row)
CREATE TABLE IF NOT EXISTS `posts` (
`post_id` int(11) NOT NULL AUTO_INCREMENT,
`title` text,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
-- posts
INSERT INTO `posts` (`id`, `title`) VALUES
(1, 'Ajax Jquery Drag and Drop Shopping Cart using PHP Mysql'),
(2, 'Make PHP Hashtag system by using Regular Expression'),
(3, 'Ajax Jquery Column Sort with PHP & MySql'),
(4, 'Drag and drop Upload multiples File By Ajax JQuery PHP');
--
CREATE TABLE IF NOT EXISTS `post_likes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` int(11) NOT NULL,
`post` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=46 ;
--table
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
INSERT INTO `user` (`id`, `name`) VALUES
(1, 'john'),
(2, 'jack');
CREATE TABLE `post_likes` (
`id` int NOT NULL,
`user` int NOT NULL,
`post_id` int NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Dumping data for table `post_likes`
--
INSERT INTO `post_likes` (`id`, `user`, `post_id`) VALUES
(11111, 3, 3),
(91, 2, 3);
final edit, i kinda just said screw it and just did this:
$query1 = 'SELECT * FROM posts WHERE post_id = "$page_id"';
$result1 = mysqli_query($connect, $query1);
$query2 = 'SELECT * FROM post_likes WHERE post_id = "$page_id"';
$result2 = mysqli_query($connect, $query2);
if ($result3 = $connect->query('SELECT * FROM post_likes WHERE post_id = "'.$page_id.'"')) {
$row_cnt = $result3->num_rows;
printf("Result set has %d rows.\n", $row_cnt);
}
thanks to all who helped though, i really appreciate it.
You can use the function ROW_NUMBER() (MySQL 8 MariaDB 10.4)
The principal thing is, that MySQL needs an order to determine which row number is what
I selected
OVER(ORDER BY COUNT(post_likes.id))
But it can be every column from the from clause
CREATE TABLE IF NOT EXISTS `posts` (
`post_id` int(11) NOT NULL AUTO_INCREMENT,
`title` text,
PRIMARY KEY (`post_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
-- posts
INSERT INTO `posts` (`post_id`, `title`) VALUES
(1, 'Ajax Jquery Drag and Drop Shopping Cart using PHP Mysql'),
(2, 'Make PHP Hashtag system by using Regular Expression'),
(3, 'Ajax Jquery Column Sort with PHP & MySql'),
(4, 'Drag and drop Upload multiples File By Ajax JQuery PHP');
--
CREATE TABLE IF NOT EXISTS `post_likes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` int(11) NOT NULL,
`post` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=46 ;
--table
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
INSERT INTO `user` (`id`, `name`) VALUES
(1, 'john'),
(2, 'jack');
WITH CTE AS (SELECT
posts.post_id,
posts.title,
COUNT(post_likes.id) AS likes,
GROUP_CONCAT(user.name SEPARATOR '|') AS liked
,ROW_NUMBER() OVER(ORDER BY COUNT(post_likes.id)) rn
FROM
posts
LEFT JOIN post_likes ON post_likes.post = posts.post_id
LEFT JOIN user ON post_likes.user = user.id
GROUP BY
posts.post_id)
SELECT post_id,title, likes,liked FROM CTE WHERE rn = 3
post_id | title | likes | liked
------: | :--------------------------------------- | ----: | :----
3 | Ajax Jquery Column Sort with PHP & MySql | 0 |
SELECT post_id,title, likes,liked FROM
(SELECT
posts.post_id,
posts.title,
COUNT(post_likes.id) AS likes,
GROUP_CONCAT(user.name SEPARATOR '|') AS liked
,ROW_NUMBER() OVER(ORDER BY COUNT(post_likes.id)) rn
FROM
posts
LEFT JOIN post_likes ON post_likes.post = posts.post_id
LEFT JOIN user ON post_likes.user = user.id
GROUP BY
posts.post_id) t1
WHERE rn = 3
post_id | title | likes | liked
------: | :--------------------------------------- | ----: | :----
3 | Ajax Jquery Column Sort with PHP & MySql | 0 |
db<>fiddle here

MYSQL Single query for multiple group by for same data

I have two query to get count and sum of rate for unique ip's.
Query one groups by date and query two groups by country
This is the table
DROP TABLE IF EXISTS `stats`;
CREATE TABLE IF NOT EXISTS `stats` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int(5) UNSIGNED NOT NULL,
`country` int(3) UNSIGNED NOT NULL,
`user_ip` int(50) UNSIGNED NOT NULL,
`timestamp` int(10) UNSIGNED NOT NULL,
`rate` int(7) UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `stats`
--
INSERT INTO `stats` (`id`, `user_id`, `country`, `user_ip`, `timestamp`, `rate`) VALUES
(1, 1, 1, 1111111111, 1489999983, 15000),
(2, 1, 2, 1111111112, 1489999984, 10000),
(3, 1, 1, 1111111111, 1489999985, 10000),
(4, 1, 1, 1111111111, 1490086333, 10000),
(5, 1, 2, 1111111111, 1490086334, 10000),
(6, 1, 1, 1111111121, 1490086335, 10000);
These are the queries I am using to get data
To get sum of rates based on date I use following query
SELECT COUNT(`user_ip`) AS `count`, SUM(`rate`) AS `rate`, `timestamp`
FROM (
SELECT DISTINCT `user_ip`, `rate`, `timestamp`
FROM `stats`.`stats`
WHERE `user_id`=? `timestamp`>=? AND `timestamp`<=?
GROUP BY DATE(FROM_UNIXTIME(`timestamp`)),`user_ip`
) c
GROUP BY DATE(FROM_UNIXTIME(`timestamp`))
Result
date count rate
20-03-2017 2 25000
21-03-2017 2 20000
To get sum of rates based on country I use following query
SELECT COUNT(`user_ip`) AS `count`, SUM(`rate`) AS `rate`, `timestamp`, `country`
FROM (
SELECT DISTINCT `user_ip`, `rate`, `timestamp`, `country`
FROM `stats`.`stats`
WHERE `user_id`=? `timestamp`>=? AND `timestamp`<=?
GROUP BY DATE(FROM_UNIXTIME(`timestamp`)),`user_ip`
) c
GROUP BY `country`
Result
country count rate
1 3 35000
2 1 10000
Since these two query are nearly same and fetches same rows from table is it possible to get both result from single query instead of two query.
Also please suggest if it can be be done in PHP effectively than MYSQL.
Thanks
Try this in php
$country="";
$groupByCondition="DATE(FROM_UNIXTIME(`timestamp`))";
if(/*BasedOnCountry*/){
$country=", `country`";
$groupByCondition = "`country`";
}
$query= "SELECT COUNT(`user_ip`) AS `count`, SUM(`rate`) AS `rate`, `timestamp`"+ $country +"
FROM (
SELECT DISTINCT `user_ip`, `rate`, `timestamp`"+ $country +"
FROM `stats`.`stats`
WHERE `user_id`=? `timestamp`>=? AND `timestamp`<=?
GROUP BY DATE(FROM_UNIXTIME(`timestamp`)),`user_ip`
) c
GROUP BY "+ $groupByCondition ;
//execute the query and get the results

Mysql get multiple counts from stats database

I need to get unique counts along with country counts and sum rate for every user
I have come up with this basic design for database where uid is user id
DROP TABLE IF EXISTS `stats`;
CREATE TABLE IF NOT EXISTS `stats` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`uid` int(5) UNSIGNED NOT NULL,
`country` int(3) UNSIGNED NOT NULL,
`ip` int(10) UNSIGNED NOT NULL,
`date` int(10) UNSIGNED NOT NULL,
`timestamp` int(10) UNSIGNED NOT NULL,
`rate` int(10) UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `stats`
(`id`, `uid`, `country`, `ip`, `date`, `timestamp`, `rate`) VALUES
(1, 1, 10, 1111111111, 2222222222, 3333333333, 100),
(2, 1, 10, 1111111112, 2222222222, 3333333333, 100),
(3, 2, 10, 1111111111, 2222222222, 3333333333, 100),
(4, 1, 10, 1111111114, 2222222223, 3333333333, 100),
(5, 1, 11, 1111111112, 2222222223, 3333333333, 100),
(6, 1, 10, 1111111111, 2222222223, 3333333333, 100);
And this is the query I am using to fetch daily counts
$query="
SELECT `uid`,
COUNT(DISTINCT `ip`)AS `count`,
`country`,
SUM(`rate`) AS `sum`,
`date`
FROM `stats`
GROUP BY `uid`, `date`
";
$result=mysqli_query($connection, $query) or trigger_error(mysqli_error($connection), E_USER_ERROR);
while($row = mysqli_fetch_assoc($result)){
echo 'userid:'.$row['uid'].' count:'.$row['count'].' country:'.$row['country'].' sum:'.$row['sum'].' date:'.$row['date'].'<br>';
};
I am getting this result
userid:1 count:2 country:10 sum:200 date:2222222222
userid:1 count:3 country:10 sum:300 date:2222222223
userid:2 count:1 country:10 sum:100 date:2222222222
Expected result
userid:1 count:2 country:10=>2 sum:200 date:2222222222
userid:1 count:3 country:10=>2, 11=>1 sum:300 date:2222222223
userid:2 count:1 country:10=>1 sum:100 date:2222222222
I guess I need something like SELECT DISTINCT country FROM stats to get country counts in main query.
Please see and suggest any possible way to do this.
Thanks
You can use subquery to achieve this:
SELECT
t.uid,
SUM(t.count) AS count,
GROUP_CONCAT(CONCAT(t.country, ' => ', t.views) SEPARATOR ', ') AS country,
SUM(t.sum) as sum,
t.date
FROM (
SELECT
s.uid,
COUNT(DISTINCT s.ip) AS count,
s.country,
COUNT(s.country) as views,
SUM(s.rate)AS sum,
s.date
FROM stats s
GROUP BY uid, date, country
) AS t
GROUP BY
t.uid,
t.date
Also available at sqlfiddle.
SUM needs a column and you gave string 'rate' in it, remove the ' from rate column name try this,
SELECT
COUNT(DISTINCT `ip`)AS `count`,
`country`,
SUM(rate) AS `sum`
FROM `stats`
GROUP BY `uid`, `date`
You will have to add country into the GROUP condition too:
SELECT
COUNT(DISTINCT `ip`) AS `count`,
`country`,
COUNT(`country`) as `countryViewsByUser`, -- added
SUM(`rate`)AS `sum`
FROM
`stats`
GROUP BY
`uid`,
`date`,
`country` -- added
You will just need to add country to your group by clause like below
$query="
SELECT
COUNT(DISTINCT `ip`)AS `count`,
`country`,
COUNT(DISTINCT `country`) AS country_count,
SUM(`rate`) AS `sum`
FROM `stats`
GROUP BY `country`, `uid`, `date`
";
And please you need to move away from mysqli_* functions, and take a look at PDO instead

Optimizing a SELECT ... UNION ... query with ORDER and LIMIT on a table with 5M+ rows

I have a table of about 5M rows of data (articles). I have the below query for fulltext searching in the title of the articles in two different languages. The problem with it is that it take about 15 seconds to be executed. MySQL version: 5.6.29-log
Here is the query:
SELECT `id`, `title`, `title_fa` FROM
(SELECT `p`.`id` AS `id`, `p`.`title` AS `title`, `p`.`title_fa` AS `title_fa`, `p`.`unique` AS `unique`, `p`.`date` AS `date` FROM `articles` `p` LEFT JOIN `authors` `a` ON `p`.`unique` = `a`.`unique` WHERE 1 AND MATCH (`p`.`title`) AGAINST ('"heat"' IN BOOLEAN MODE)
UNION
SELECT `p`.`id` AS `id`, `p`.`title` AS `title`, `p`.`title_fa` AS `title_fa`, `p`.`unique` AS `unique`, `p`.`date` AS `date` FROM `articles` `p` LEFT JOIN `authors` `a` ON `p`.`unique` = `a`.`unique` WHERE 1 AND MATCH (`p`.`title_fa`) AGAINST ('"گرما"' IN BOOLEAN MODE)) AS `subQuery`
GROUP BY `unique` ORDER BY `date` DESC LIMIT 0,10;
This is the table structure:
CREATE TABLE `articles` (
`id` int(10) unsigned NOT NULL,
`title` text COLLATE utf8_persian_ci NOT NULL,
`title_fa` text COLLATE utf8_persian_ci NOT NULL,
`description` text COLLATE utf8_persian_ci NOT NULL,
`description_fa` text COLLATE utf8_persian_ci NOT NULL,
`date` date NOT NULL,
`unique` tinytext COLLATE utf8_persian_ci NOT NULL,
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci;
ALTER TABLE `articles`
ADD PRIMARY KEY (`id`),
ADD KEY `unique` (`unique`(128)),
ADD FULLTEXT KEY `TtlDesc` (`title`,`description`);
ADD FULLTEXT KEY `Title` (`title`);
ADD FULLTEXT KEY `faTtlDesc` (`title_fa`,`description_fa`);
ADD FULLTEXT KEY `faTitle` (`title_fa`);
MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT;
First improvement step:
By searching is SO I came across this post:
Combining UNION and LIMIT operations in MySQL query
Using the proposed method I changed my query as follow:
SELECT `id`, `title`, `title_fa` FROM
(SELECT `p`.`id` AS `id`, `p`.`title` AS `title`, `p`.`title_fa` AS `title_fa`, `p`.`date` AS `date`, `p`.`unique` AS `unique` FROM `articles` `p` LEFT JOIN `authors` `a` ON `p`.`unique` = `a`.`unique` WHERE MATCH (`p`.`title`) AGAINST ('"heat"' IN BOOLEAN MODE) LIMIT 0,100
UNION
SELECT `p`.`id` AS `id`, `p`.`title` AS `title`, `p`.`title_fa` AS `title_fa`, `p`.`date` AS `date`, `p`.`unique` AS `unique` FROM `articles` `p` LEFT JOIN `authors` `a` ON `p`.`unique` = `a`.`unique` WHERE MATCH (`p`.`title_fa`) AGAINST ('"گرما"' IN BOOLEAN MODE) LIMIT 0,100) AS `subQuery`
GROUP BY `unique` ORDER BY `date` DESC LIMIT 0,10
The performance was astonishing and the query took like 0.04 seconds to execute. The problem was with sorting that I liked to have more recent articles listed first but this query is not able to do so. Also I'm not sure how the next set of results (i.e. the next 10 results - second page of the results) could be retrieved and shown.
Second improvement step:
Searching more on SO I came across this one:
SQL Query - Using Order By in UNION
And my query looked like below:
SELECT `id`, `title`, `title_fa`, `unique`, `date` FROM
(SELECT `p`.`id` AS `id`, `p`.`title` AS `title`, `p`.`title_fa` AS `title_fa`, `p`.`date` AS `date`, `p`.`unique` AS `unique` FROM `articles` `p` LEFT JOIN `authors` `a` ON `p`.`unique` = `a`.`unique` WHERE MATCH (`p`.`title`) AGAINST ('"heat"' IN BOOLEAN MODE) ORDER BY `p`.`date` DESC LIMIT 0,20) AS `subQueryE`
UNION ALL
SELECT `id`, `title`, `title_fa`, `unique`, `date` FROM
(SELECT `f`.`id` AS `id`, `f`.`title` AS `title`, `f`.`title_fa` AS `title_fa`, `f`.`date` AS `date`, `f`.`unique` AS `unique` FROM `articles` `f` LEFT JOIN `authors` `a` ON `f`.`unique` = `a`.`unique` WHERE MATCH (`f`.`title_fa`) AGAINST ('"گرما"' IN BOOLEAN MODE) ORDER BY `f`.`date` DESC LIMIT 0,20) AS `subQueryF`
GROUP BY `unique` ORDER BY `date` DESC LIMIT 0,10
The performance was better but not satisfying as it took about 7 seconds. It brought up another problem that even having GROUP BY unique duplicate rows was still present in the results.
Third step:
I did another test by executing the following query hoping to get better results:
SELECT `p`.`id` AS `id`, `p`.`title` AS `title`, `p`.`title_fa` AS `title_fa`, `p`.`date` AS `date`, `p`.`unique` AS `unique` FROM `articles` `p` LEFT JOIN `authors` `a` ON `p`.`unique` = `a`.`unique` WHERE MATCH (`p`.`title`) AGAINST ('"heat"' IN BOOLEAN MODE) OR MATCH (`p`.`title_fa`) AGAINST ('"گرما"' IN BOOLEAN MODE) GROUP BY `unique` ORDER BY `date` DESC LIMIT 0,10
But the execution time was awful and reached more than 100 seconds.
Any help is more than welcome and thanks in advance.

MySQL Retrieve data IF column is not empty

I have this table:
title1 title2 type
---------------------
qwe1 xcv2 3
asd1 tzu 7
fgh1 (empty) 4
and I have this query:
SELECT `title1` AS `title`, `type`
FROM `table`
UNION
SELECT `title2` AS `title`, `type`
FROM `table`
ORDER BY `title` ASC, type
(please see fiddle here).
How can I transform this query so that it doesn't display any rows if title2 is empty? Row 1 shouldn't be displayed.
I tried to implement an IF-statement, but I always get a mysql error.
This should cover the empty string and null:
SELECT `title1` AS `title`, `type`
FROM `table`
UNION
SELECT `title2` AS `title`, `type`
FROM `table` where title2 is not null and title2 <> ''
ORDER BY `title` ASC, type
You should use WHERE statement to define condition for your query
SELECT `title1` AS `title`, `type`
FROM `table`
WHERE `title1` IS NOT NULL AND LENGTH(`title1`) > 0
UNION
SELECT `title2` AS `title`, `type`
FROM `table`
WHERE `title2` IS NOT NULL AND LENGTH(`title2`) > 0
ORDER BY `title` ASC, type
When you use UNION, you remove duplicates. Are you sure you don't want to use UNION ALL? In addition, is there any reason that you need to have a title2? It doesn't seem normalized to me.
SELECT title1 AS title, `type`
FROM `table`
UNION ALL
SELECT title2 AS title, `type`
FROM `table`
WHERE title2 IS NOT NULL AND title2 <> ''
See the demo
I think that, instead to check if title2 is not null and title2 <> '', the shortest way is to use COALESCE to convert any Null value to empty, and then check if the result of COALESCE is <> '', like this:
SELECT title1 AS title, `type`
FROM `table`
UNION
SELECT title2 AS title, `type`
FROM `table`
WHERE COALESCE(title2, '') <> ''
and if you don't need to remove duplicates, i think it's better to use UNION ALL instead of UNION.

Categories