How to store multiple dynamic values within one field in MySQL? - php

How can I go about storing multiple values (numbers and words) within one field of a MySQL database and then extracting them again as and when I need them using MySQL and PHP?
For example, I want to store the dynamic values a user will enter using a form for example 1, 2, foo, tree, and monkey all in the same field in a database.
Then I want to extract it and put them on separate lines for example:
1
2
foo
tree
monkey
Any ideas?

MySQL 5.7.8 has a new data type that is JSON. You can store a JSON string with all the user information in that column.
Example:
CREATE TABLE table1 (jsonString JSON);
INSERT INTO table1 VALUES('{"car": "bmw", "year": "2006", "key": "value" }');
MySQL Reference

You can put all the values into an array and then serialize it:
$string = serialize(array(1, 2, 'foo', 'tree', 'monkey');
This will give you a string which you store in your database. Later, you can recover your array with de-serializing it:
$array = unserialize($string);

If you're referring to a datatype which can handle a whole slew of stuff, you can use text otherwise this is a bad idea and this is not how you should be storing data in a normalized relational database. Can you please provide information on what you're storing?
I'm a SQL noob myself so if any guru has a better schema strategy, let me know.. this is what I came up with:
Dump:
/*
Navicat MySQL Data Transfer
Date: 2009-10-20 03:01:18
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `job_scores`
-- ----------------------------
DROP TABLE IF EXISTS `job_scores`;
CREATE TABLE `job_scores` (
`job_id` int(2) NOT NULL,
`user_id` int(2) NOT NULL,
`rating` tinyint(2) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-- ----------------------------
-- Records of job_scores
-- ----------------------------
INSERT INTO `job_scores` VALUES ('1', '1', '10');
-- ----------------------------
-- Table structure for `jobs`
-- ----------------------------
DROP TABLE IF EXISTS `jobs`;
CREATE TABLE `jobs` (
`id` int(2) NOT NULL auto_increment,
`name` varchar(50) collate utf8_unicode_ci default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-- ----------------------------
-- Records of jobs
-- ----------------------------
INSERT INTO `jobs` VALUES ('1', 'plumber');
-- ----------------------------
-- Table structure for `users`
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(2) NOT NULL auto_increment,
`name` varchar(50) collate utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('1', 'John');
Example query:
SELECT
jobs.name as job_name, users.name as user_name, job_scores.rating
FROM
job_scores
INNER JOIN jobs ON jobs.id = job_scores.job_id
INNER JOIN users on users.id = job_scores.user_id
WHERE
user_id = 1
Result:
plumber John 10

Related

Show all user posts in one page but query is too slow

I try to show the videos and pictures shared by the members in one page. The query I wrote works fine but is very slow. I don't know why this happened. That's why I need your help. You can also see the conditions I wrote in the following query.
Here is the sqlFiddle
$morequery="";
if($lastpostid) {
$morequery=" AND P.user_post_id<'".$lastpostid."' ";
}
$GetAllPostQuery = mysqli_query($this->db,"SELECT
P.user_post_id,
P.user_id_fk,P.post_type,
P.who_can_see_post,
P.post_image_id,P.post_video_id,
P.post_video_name,
U.user_name, U.user_fullname,U.influencer_status
FROM user_posts P
INNER JOIN users U
ON P.user_id_fk = U.user_id
WHERE
U.user_status='1' AND
U.influencer_status = '1' AND
(P.who_can_see_post IN('everyone','influencer','friends')) AND
(P.post_type IN('image','video')) $morequery
ORDER BY
P.user_post_id
DESC LIMIT " .$this->perpage) or die(mysqli_error($this->db));
//Store the result
while($row=mysqli_fetch_array($GetAllPostQuery)) {
// Store the result into array
$data[]=$row;
}
if(!empty($data)) {
// Store the result into array
return $data;
}
The Users Table Here:
-- phpMyAdmin SQL Dump
-- version 4.8.3
-- https://www.phpmyadmin.net/
--
-- Host: localhost:8889
-- Generation Time: Sep 10, 2019 at 11:56 AM
-- Server version: 5.7.23
-- PHP Version: 7.2.10
CREATE TABLE `users` (
`user_id` int(11) NOT NULL,
`user_name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`user_status` enum('0','1','2','3') NOT NULL DEFAULT '0',
`user_fullname` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`influencer_status` enum('0','1') NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `users`
--
ALTER TABLE `users`
ADD PRIMARY KEY (`user_id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `users`
--
ALTER TABLE `users`
MODIFY `user_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1;
And The User Posts Table Here:
CREATE TABLE `user_posts` (
`user_post_id` int(11) NOT NULL,
`user_id_fk` int(11) DEFAULT NULL,
`post_type` enum('text','image','link’,’video','audio','avatar','cover','gif','location','watermark','which','page','event','blog','group','product','bfaf','inf') NOT NULL DEFAULT 'text',
`post_created_time` int(11) NOT NULL DEFAULT '1524910573',
`who_can_see_post` enum('everyone','onlyme','friends','influencer') NOT NULL DEFAULT 'everyone',
`post_video_id` varchar(255) DEFAULT NULL,
`post_video_name` varchar(255) DEFAULT NULL,
`post_audio_id` varchar(255) DEFAULT NULL,
`post_image_id` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Indexes for table `user_posts`
--
ALTER TABLE `user_posts`
ADD PRIMARY KEY (`user_post_id`),
ADD KEY `ex_posts` (`user_id_fk`);
--
-- AUTO_INCREMENT for table `user_posts`
--
ALTER TABLE `user_posts`
MODIFY `user_post_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1;
--
-- Constraints for table `user_posts`
--
ALTER TABLE `user_posts`
ADD CONSTRAINT `ex_posts` FOREIGN KEY (`user_id_fk`) REFERENCES `users` (`user_id`);
What should I do to make the query work faster? Can you help me.
The fiddle contains too few rows to reproduce the problem but these 2 indexes might help.it really depends on your data distribution.
ALTER TABLE users ADD KEY(user_status,influencer_status);
ALTER TABLE user_posts ADD KEY(who_can_see_post,post_type);

Mysql query join with table column

I have a scenario where i have one main table. Main table has 2 extra columns one is for table name (child table name) and other is for table id (child table id). when we enter the value in main table we also tell enter value in child table and then we enter the name of the table in main table name field and child id in the child field of the main table.
now when i query i need to join query with child table in a way that i picks up the table name from the column and join query with that table with concat function and then join on child id.
below is the structure of the table and also there values
CREATE TABLE IF NOT EXISTS `tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`tbl_type` enum('multi','gift','pledge') DEFAULT NULL,
`tbl_type_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
INSERT INTO `tbl` (`id`, `timestamp`, `tbl_type`, `tbl_type_id`) VALUES
(1, '2015-03-09 09:39:42', '', 1),
(2, '2015-03-09 22:43:23', 'multi', 2),
(3, '2015-03-09 23:26:38', 'gift', 1),
(4, '2015-03-10 09:46:15', 'pledge', 2);
-- --------------------------------------------------------
CREATE TABLE IF NOT EXISTS `tbl_gift` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`amount` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `tbl_gift` (`id`, `amount`) VALUES
(1, '1231200'),
(2, '1231200');
-- --------------------------------------------------------
CREATE TABLE IF NOT EXISTS `tbl_multi` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`amount` float(255,0) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
-- --------------------------------------------------------
CREATE TABLE IF NOT EXISTS `tbl_pledge` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`amount` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `tbl_pledge` (`id`, `amount`) VALUES
(1, '10000'),
(2, '10200');
so this is simple hard code query
select * from tbl t left join tbl_gift g on g.id = t.tbl_type_id
but i want to make it dynamic i tried this
select * from tbl t left join (concat('tbl', '_', t.tbl_type)) g on g.id = t.tbl_type_id
should get the table which i need
(concat('tbl', '_', t.tbl_type))
but it get error
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '('tbl', '_', t.tbl_type)) g on g.id = t.tbl_type_id LIMIT 0, 30' at line 1
The comments by Ankit and Usedby answered your question.
SQL does not allow you to provide dynamically constructed table names as you attempted. They provided you with two options: 1) Construct your query dynamically on the PHP side, then SQL see only the static table names or
2) Use the SQL PREPARE command to construct the dynamic table name and the EXECUTE SQL command to execute it.

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'

Duplicate row in database with relational tables

I am trying to duplicate a page in the database and all related rows.
The problem I am having is because the page_group_id is an identifier for both tables. Is there any way of doing this without looping each of the new "page_groups" records?
pages (page_id, page_name, etc)
page_groups (page_group_id, page_id, etc)
page_group_items (page_group_id, item_id, etc)
UPDATE:
--
-- Table structure for table `pages`
--
CREATE TABLE IF NOT EXISTS `pages` (
`page_id` int(11) NOT NULL AUTO_INCREMENT,
`page_name` varchar(255) NOT NULL,
PRIMARY KEY (`page_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
-- Dumping data for table `pages`
--
INSERT INTO `pages` (`page_id`, `page_name`) VALUES
(1, 'My Page'),
(2, 'My other page');
-- --------------------------------------------------------
--
-- Table structure for table `page_groups`
--
CREATE TABLE IF NOT EXISTS `page_groups` (
`page_group_id` int(11) NOT NULL AUTO_INCREMENT,
`page_group_name` varchar(255) NOT NULL,
`page_id` int(11) NOT NULL,
PRIMARY KEY (`page_group_id`),
KEY `page_id` (`page_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
--
-- Dumping data for table `page_groups`
--
INSERT INTO `page_groups` (`page_group_id`, `page_group_name`, `page_id`) VALUES
(1, 'My Group', 1),
(2, 'My Group', 2);
-- --------------------------------------------------------
--
-- Table structure for table `page_group_items`
--
CREATE TABLE IF NOT EXISTS `page_group_items` (
`page_group_id` int(11) NOT NULL,
`item_id` int(11) NOT NULL,
KEY `item_id` (`item_id`),
KEY `page_group_id` (`page_group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `page_group_items`
--
INSERT INTO `page_group_items` (`page_group_id`, `item_id`) VALUES
(1, 1),
(1, 2),
(1, 3),
(2, 1),
(2, 2);
Since you're replacing the only unique identifier in each table (the primary key) when copying, I can't see a way of doing it without adding temporary anchor columns to the tables, do the copy and remove them again. Something like this;
ALTER TABLE pages ADD originalpageid INT;
UPDATE pages set originalpageid=page_id;
ALTER TABLE page_groups ADD originalpagegroupid INT;
UPDATE page_groups SET originalpagegroupid=page_group_id;
INSERT INTO pages (page_name,originalpageid)
SELECT page_name,originalpageid FROM pages;
INSERT INTO page_groups (page_group_name,page_id,originalpagegroupid)
SELECT page_group_name,MAX(pages.page_id),originalpagegroupid
FROM page_groups
JOIN pages
ON page_groups.page_id=originalpageid
GROUP BY originalpageid,page_group_name,originalpagegroupid;
INSERT INTO page_group_items(page_group_id,item_id)
SELECT MAX(page_groups.page_group_id),item_id
FROM page_group_items
JOIN page_groups
ON page_group_items.page_group_id=originalpagegroupid
GROUP BY originalpagegroupid,item_id;
ALTER TABLE pages DROP COLUMN originalpageid;
ALTER TABLE page_groups DROP COLUMN originalpagegroupid;
An SQLfiddle to test with
If the use case is doing it all the time in the system, it may not be the solution you're looking for, but for manual intervention it should work well.
As always, always back your database up before running SQL from random strangers on the Internet :)

Mysql query with many join's takes long time to load

I am using this query to get record
SELECT *
FROM (`content` as c)
LEFT JOIN `content_assets` AS ca ON `ca`.`contentid` = `c`.`id`
LEFT JOIN `assets` AS a ON `a`.`act_id` = `ca`.`actorid`
INNER JOIN `content_gens` AS cg ON `cg`.`contentid` = `c`.`id`
INNER JOIN `gens` AS g ON `cg`.`genid` = `g`.`gen_id`
LEFT JOIN `live_data` AS l ON `l`.`contentid` = `c`.`id`
LEFT JOIN `live_type` AS lt ON `lt`.`lt_id` = `l`.`live_type`
LEFT JOIN `returned` AS r ON `r`.`content_id` = `c`.`id`
WHERE `c`.`id` = '14175'
my live_data table is main issue here which is causing this load time, it contains about 200k records and its taking 20-40 seconds to load a page. If I dont join that table everything is fine. I am currently using file based caching to cache result from above query but I would like to know if above query can be optimized more.
Edit:
My table indexes can be seen here.
-- phpMyAdmin SQL Dump
-- version 3.4.5
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Generation Time: Apr 08, 2013 at 11:07 PM
-- Server version: 5.5.16
-- PHP Version: 5.3.8
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
--
-- Database: `database`
--
-- --------------------------------------------------------
--
-- Table structure for table `assets`
--
CREATE TABLE IF NOT EXISTS `assets` (
`act_id` int(11) NOT NULL AUTO_INCREMENT,
......
PRIMARY KEY (`act_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=8726 ;
-- --------------------------------------------------------
--
-- Table structure for table `content`
--
CREATE TABLE IF NOT EXISTS `content` (
`id` int(11) NOT NULL AUTO_INCREMENT,
.........
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=14267 ;
-- --------------------------------------------------------
--
-- Table structure for table `content_asset`
--
CREATE TABLE IF NOT EXISTS `content_asset` (
`contentid` int(11) NOT NULL,
`actorid` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `content_gener`
--
CREATE TABLE IF NOT EXISTS `content_gener` (
`contentid` int(11) NOT NULL,
`genid` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `gens`
--
CREATE TABLE IF NOT EXISTS `gens` (
`gen_id` int(11) NOT NULL AUTO_INCREMENT,
......
PRIMARY KEY (`gen_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=65 ;
-- --------------------------------------------------------
--
-- Table structure for table `live_data`
--
CREATE TABLE IF NOT EXISTS `live_data` (
`link_id` int(11) NOT NULL AUTO_INCREMENT,
.....
PRIMARY KEY (`link_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=214014 ;
-- --------------------------------------------------------
--
-- Table structure for table `live_type`
--
CREATE TABLE IF NOT EXISTS `live_type` (
`lt_id` int(11) NOT NULL AUTO_INCREMENT,
....
PRIMARY KEY (`lt_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;
-- --------------------------------------------------------
--
-- Table structure for table `returned`
--
CREATE TABLE IF NOT EXISTS `returned` (
`r_id` int(11) NOT NULL AUTO_INCREMENT,
....
PRIMARY KEY (`r_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
And Explain query result:
Thanks for any help.
You should add indexes on following columns:
live_data.contentid
content_asset.contentid
content_gener.contentid
And convert your big tables to InnoDB using ALTER TABLE table_name ENGINE = InnoDB.
Also it is often a good idea to use foreign keys.
If you run explain [your full query] in the MySQL console you will see what indexes are used. Depending on the size of each table in the join you want to have indexes on the columns used to connect the tables in the join.

Categories