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

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

Related

SQL_CALC_FOUND_ROWS Returns 0 in SELECT INSERT Query - Doctrine

Below are my table data
TABLE Name: table_1
id value created_date
------ ------ ---------------------
TABLE Name: table_2
id value created_date
------ ------ ---------------------
1 A 2021-12-17 17:22:55
2 B 2021-12-17 17:22:56
3 C 2021-12-17 17:22:57
4 D 2021-12-17 17:22:58
When I execute the below query in SQL Tool, I am getting FOUND_ROWS() value.
INSERT INTO table_1 (`value`)
SELECT SQL_CALC_FOUND_ROWS `value` FROM table_2;
SELECT FOUND_ROWS();
When I tried to execute in PHP Doctrine statements. It is not working!
$statement = $entityManager->getConnection()->prepare("INSERT INTO table_1 (`value`)
SELECT SQL_CALC_FOUND_ROWS `value` FROM table_2 LIMIT 1000");
$statement->execute();
$statementFoundRow = $entityManager->getConnection()->prepare("SELECT FOUND_ROWS()");
$statementFoundRow->execute();
echo $foundRow = $statementFoundRow->fetchColumn(0);exit;
Output: 0
But I am getting values when I am not using the INSERT Query.
$statement = $entityManager->getConnection()->prepare("SELECT SQL_CALC_FOUND_ROWS `value` FROM table_2 LIMIT 1000");
$statement->execute();
$statementFoundRow = $entityManager->getConnection()->prepare("SELECT FOUND_ROWS()");
$statementFoundRow->execute();
echo $foundRow = $statementFoundRow->fetchColumn(0);exit;
Output: 4
Can someone help me with 0-row issue?
TABLE DATA for reference:
CREATE TABLE `table_1` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`value` varchar(255) DEFAULT NULL,
`created_date` datetime DEFAULT current_timestamp(),
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
CREATE TABLE `table_2` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`value` varchar(255) DEFAULT NULL,
`created_date` datetime DEFAULT current_timestamp(),
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
INSERT INTO `table_2`(`id`,`value`,`created_date`) VALUES
(1,'A','2021-12-17 17:18:15'),
(2,'B','2021-12-17 17:18:16'),
(3,'C','2021-12-17 17:18:17'),
(4,'D','2021-12-17 17:18:19');

Missing rows due to too strict WHERE statement

In the example code below we have attempted to minimize our problem into a simplified test case.
We have 3 tables: users, images and ratings.
1: We want to print all entries from the ‘ratings’ table.
2: In the ‘images’ table, there can be several images uploaded by the same users into different sections of the site, e.g. ‘user_profile_picture’ or ‘user_cover_photo’.
3: When printing the ratings, we want to fetch entries from the ‘images’ table if the user has uploaded a profile picture (section = ‘profile_picture’).
The WHERE statement in the query in the example code fails for users with no profile pictures uploaded.
How can we make sure to print all entries, as well as only grab the image connected to the user if section = profile_picture …?
CREATE TABLE `test_ratings` (
`id` int(4) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int(4) UNSIGNED NOT NULL,
`rating` enum('1','2','3','4','5') DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `test_ratings` (`id`, `user_id`, `rating`) VALUES
(2, 2, '4'),
(1, 1, '5');
CREATE TABLE `test_users` (
`id` int(4) UNSIGNED NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `test_users` (`id`, `username`) VALUES
(1, 'Test person 1'),
(2, 'Test person 2');
CREATE TABLE `test_users_images` (
`id` int(4) UNSIGNED NOT NULL AUTO_INCREMENT,
`filename` varchar(80) NOT NULL,
`section` enum('user_cover_photo','user_profile_picture') DEFAULT NULL,
`user_id` int(4) UNSIGNED DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `test_users_images` (`id`, `filename`, `section`, `user_id`) VALUES
(1, 'rtfos.jpg', 'user_profile_picture', 2),
(2, 'exer8.jpg', 'user_cover_photo', 2);
<?php
$mysqli = new mysqli("localhost", "root", "", "website");
$result = $mysqli->query('SELECT image.filename, user.username, ratings.rating
FROM test_ratings ratings
INNER JOIN test_users user ON user.id = ratings.user_id
INNER JOIN test_users_images image ON image.user_id = user.id
WHERE image.section = \'user_profile_picture\'');
while ($row = $result->fetch_object()) {
echo '<p>' . $row->username . ' - ' . $row->filename . '</p>';
}
?>
As already commented, start with the user table and left join to it. Include the image section filter in the ON clause of the respective join.
SELECT image.filename,
user.username,
ratings.rating
FROM test_users user
LEFT JOIN test_users_images image
ON image.user_id = user.id
AND image.section = 'user_profile_picture'
LEFT JOIN test_ratings ratings
ON user.id = ratings.user_id;
db<>fiddle
P.S.: It's a WHERE clause, not a statement.
By changing the INNER JOIN to a LEFT JOIN for image.sections you keep all rows from ratings (if the user still exists in the database). To include users without a profile image you have to add a condition that image.section is allowed to be NULL.
SELECT image.filename, user.username, ratings.rating
FROM test_ratings ratings
INNER JOIN test_users user ON user.id = ratings.user_id
LEFT JOIN test_users_images image ON image.user_id = user.id
WHERE image.section = 'user_profile_picture' OR image.section IS NULL
db<>fiddle

Select rows that match right side of one to many reationship in MySql

I have 4 tables. One for companies, one for products one for company address, and one for company directors.
The products, director and address tables are in a one to many relationship to the company table.
So one company can have many products, many addresses and many directors.
CREATE TABLE IF NOT EXISTS `companies` (
`company_id` int(11) NOT NULL AUTO_INCREMENT,
`company_name` varchar(50) NOT NULL,
PRIMARY KEY (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
CREATE TABLE IF NOT EXISTS `products` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`product` varchar(50) NOT NULL,
PRIMARY KEY (`product_id`),
KEY `company_id` (`company_id`),
KEY `product` (`product`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
CREATE TABLE IF NOT EXISTS `directors` (
`director_id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`surname` varchar(100) NOT NULL,
`dob` date NOT NULL,
PRIMARY KEY (`director_id`),
KEY `company_id` (`company_id`),
KEY `surname` (`surname`),
KEY `dob` (`dob`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
CREATE TABLE IF NOT EXISTS `addresses` (
`address_id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(1) NOT NULL,
`postcode` varchar(10) NOT NULL,
PRIMARY KEY (`address_id`),
KEY `company_id` (`company_id`),
KEY `postcode` (`postcode`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=11 ;
INSERT INTO `companies` (`company_id`, `company_name`) VALUES
(1, 'Honda'),
(2, 'Toyota');
INSERT INTO `products` (`product_id`, `company_id`, `product`) VALUES
(1, 1, 'Civic'),
(2, 1, 'Accord'),
(3, 2, 'Corolla'),
(4, 2, 'Prius'),
(5, 1, 'CRV');
INSERT INTO `directors` (`director_id`, `company_id`, `surname`, `dob`) VALUES
(1, 1, 'Jones', '1990-09-09'),
(2, 1, 'Smith', '1980-08-08'),
(3, 2, 'Lucas', '1970-07-07'),
(4, 1, 'Kelly', '1960-06-06'),
(5, 2, 'Monty', '1950-05-05');
INSERT INTO `addresses` (`address_id`, `company_id`, `postcode`) VALUES
(6, 1, '12345'),
(7, 2, '23456'),
(8, 1, '34567'),
(9, 2, '45678'),
(10, 1, '56789');
Im trying to write an efficient query (using MySql / PDO) to find products for companies that match match directors (surname AND dob) and addresses (postcode).
I just want to list one matching product per row, not list every director or postcode separately.
So far I have the below query, which seems to work, but it's ugly and I suspect a ridiculous way to go about this in terms of speed and efficiency.
SELECT product
FROM products p
LEFT JOIN companies c USING(company_id)
WHERE :lname IN (
SELECT surname
FROM directors d
WHERE c.company_id = d.company_id )
AND :dob IN (
SELECT dob
FROM directors d
WHERE c.company_id = d.company_id )
AND :postcode IN (
SELECT postcode
FROM addresses a
WHERE c.company_id = a.company_id )
Thank you in advance for your help.
Unsure why you need subqueries at all?
SELECT p.product FROM products p
INNER JOIN companies c USING(company_id)
INNER JOIN directors d ON d.company_id = c.company_id AND d.surname = 'Jones' AND d.dob = '1990-09-09'
INNER JOIN addresses a ON a.company_id = c.company_id AND a.postcode = '12345'
Or
SELECT p.product FROM products p
INNER JOIN companies c USING(company_id)
INNER JOIN directors d USING(company_id)
INNER JOIN addresses a USING(company_id)
WHERE d.surname = 'Jones'
AND d.dob = '1990-09-09'
AND a.postcode = '12345'
If you do an EXPLAIN on these two queries, you'll see they end up the same internally.
At the very least, the two subqueries on directors can be unified by rewriting them with the exists operator instead of in. For good measures, I rewrote the entire query with this operator, although it's not strictly necessary:
SELECT product
FROM products p
LEFT JOIN companies c USING(company_id)
WHERE EXISTS (SELECT *
FROM directors d
WHERE c.company_id = d.company_id AND
(:lname = d.lanme OR :dob = d.dob)) AND
EXISTS (SELECT *
FROM addresses a
WHERE c.company_id = a.company_id AND :postcode = a.postcode)

get the count of field and group concat

table structure is as follows
-- Table structure for table category
CREATE TABLE `category` (
`cat_id` int(10) NOT NULL auto_increment,
`heading` varchar(255) NOT NULL,
PRIMARY KEY (`cat_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `category` (`cat_id`, `heading`) VALUES
(1, 'Fashion'),
(2, 'Kids');
-- --------------------------------------------------------
-- Table structure for table `shop`
CREATE TABLE `shop` (
`store_id` int(10) NOT NULL auto_increment,
`shop_name` varchar(255) NOT NULL,
`cat_id` int(10) NOT NULL,
`subcat_id` int(10) NOT NULL,
PRIMARY KEY (`store_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
INSERT INTO `shop` (`store_id`, `shop_name`, `cat_id`, `subcat_id`) VALUES
(1, 'Test Store', 1, 1),
(2, 'Test Store 1', 1, 1),
(3, 'Another Store', 1, 3);
-- --------------------------------------------------------
-- Table structure for table `subcategory`
CREATE TABLE `subcategory` (
`subcat_id` int(10) NOT NULL auto_increment,
`cat_id` int(10) NOT NULL,
`heading` varchar(255) NOT NULL,
PRIMARY KEY (`subcat_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
INSERT INTO `subcategory` (`subcat_id`, `cat_id`, `heading`) VALUES
(1, 1, 'Women'),
(2, 1, 'General'),
(3, 1, 'Men'),
(4, 2, 'Children');
if i use the below query i get the following output
SELECT
`category`.`heading` AS `category`
, `subcategory`.`heading` AS `subcategory`
, COUNT(`shop`.`subcat_id`) AS cnt
FROM
`test`.`shop`
INNER JOIN `test`.`subcategory`
ON (`shop`.`subcat_id` = `subcategory`.`subcat_id`)
INNER JOIN `test`.`category`
ON (`shop`.`cat_id` = `category`.`cat_id`)
GROUP BY `shop`.`subcat_id`
HAVING (COUNT(`shop`.`subcat_id`) !='');
categorysubcategorycnt
FashionWomen2
FashionMen1
but i want to group concat the subcategory like below
categorysubcategory
FashionWomen,2|Men,1
Try this
SELECT t.category,
GROUP_CONCAT(CONCAT(t.subcategory,',',t.cnt) SEPARATOR '|') `concat`
FROM (
SELECT
`category`.`heading` AS `category`
, `subcategory`.`heading` AS `subcategory`
, COUNT(`shop`.`subcat_id`) AS cnt
FROM
`shop`
INNER JOIN `subcategory`
ON (`shop`.`subcat_id` = `subcategory`.`subcat_id`)
INNER JOIN `category`
ON (`shop`.`cat_id` = `category`.`cat_id`)
GROUP BY `shop`.`subcat_id`
) t
GROUP BY t.category
Note group concat has a default limit of 1024 character but it can be increased by following the manual
Fiddle Demo
Not a recommended output format, but easily done with a nested subquery:
SELECT category,
group_concat(subcategory, ',', cnt separator '|') as vals
FROM (SELECT c.`heading` AS `category`, sc.`heading` AS `subcategory`,
COUNT(`shop`.`subcat_id`) AS cnt
FROM `test`.`shop` s INNER JOIN
`test`.`subcategory` sc
ON s.`subcat_id` = sc.`subcat_id`) INNER JOIN
`test`.`category` c
ON s.`cat_id` = c.`cat_id`
GROUP BY c.`heading`, sc.`heading`
) sc
GROUP BY category;
Your having clause is unnecessary. It is just checking that there is at least one row for each group. But there is one, because you are using inner join.

how to select from database based on a match in category?

Is it possible to select certain rows based on a category which matches it when there are multiple categories in the entry? It's hard to explain so I'll show you. The row I have in the database looks like this:
**article_title** | **article_content** | **category**
Article-1 | some content here | one,two,three,four
So my query looks like this:
$sql = mysqli_query($mysqli_connect, "SELECT * FROM table WHERE category='
preg_match(for example the word three)'");
Reason why I'm doing that is some articles will be available on multiple pages like page one and page three...so is there a way to match what I'm looking for through the entry in the database row?
You should use a more flexible database design. Create a separate table that holds the one-to-many relationships between (one) article and (many) categories:
CREATE TABLE IF NOT EXISTS `articles` (
`article_id` int(11) NOT NULL AUTO_INCREMENT,
`article_name` varchar(255) NOT NULL,
PRIMARY KEY (`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
INSERT INTO `articles` (`article_id`, `article_name`) VALUES
(1, 'Research Normalized Database Design');
CREATE TABLE IF NOT EXISTS `article_category` (
`article_id` int(11) NOT NULL,
`category_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `article_category` (`article_id`, `category_id`) VALUES
(1, 1),
(1, 2);
CREATE TABLE IF NOT EXISTS `categories` (
`category_id` int(11) NOT NULL AUTO_INCREMENT,
`category_name` varchar(255) NOT NULL,
PRIMARY KEY (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `categories` (`category_id`, `category_name`) VALUES
(1, 'Databases'),
(2, 'Normalization');
Querying then becomes as simple as:
SELECT
*
FROM
articles AS a
JOIN
article_category AS pivot ON a.article_id = pivot.article_id
WHERE
pivot.category_id = 2
Or do something like:
SELECT
*
FROM
articles AS a
JOIN
article_category AS pivot ON a.article_id = pivot.article_id
JOIN
categories AS c ON pivot.category_id = c.category_id
WHERE
c.category_name = 'Normalization'

Categories