I need some help with an MySQL statement that I cannot really make to work.
I have a table like this:
+---+-------------------------------+
|id | fruit |
+---+-------------------------------+
| 1 | apple,orange,pear,grape,lemon |
| 2 | pear,melon,apple,kiwi,lemon |
| 3 | orange,kiwi,pear,apple,cherry |
| 4 | grape,lemon,cherry,apple,melon|
+---+-------------------------------+
What I need to do is to SELECT all rows where the column fruit contains the word melon. The word in question might be at any position in the array.
I tired with the below query but for some reason I only get 3-4 rows, definitely not all of them:
$fruit = $_GET['fruit'];
$query1= "SELECT * FROM tbl_fruits WHERE ".$fruit." IN (fruit)";
Any comments will be appreciated.
You can use FIND_IN_SET
SELECT * FROM tbl_fruits
WHERE find_in_set('$fruit', fruit)
But you actually should rather change your table design.
Never store multiple values in a single column!
A better table design would be
fruits table
------------
id name
1 melon
2 orange
3 apple
...
products table
-------------------
id name price
1 P1 1.50
2 P2 2.99
3 P3 0.99
product_fruits table
--------------------
product_id fruit_id
1 1
1 2
2 2
3 1
That is a classic many to many relation (m to n).
$fruit = $_GET['fruit'];
$query1= sprintf("SELECT * FROM tbl_fruits WHERE fruit LIKE '%%s%'",
mysql_real_escape_string($fruit ));
Try below sql
$fruit = $_GET['fruit'];
$query1= "SELECT * FROM tbl_fruits WHERE fruit LIKE '%".$fruit."%'";
Use a LIKE clause:
SELECT * FROM tbl_fruits
WHERE fruit LIKE '%apple%';
select * from tbl_fruits WHERE fruit like '%melon%'
As mentioned before you can/should build your table structure differently.
It's the "relational" in "relational database". see http://en.wikipedia.org/wiki/Database_normalization
As always: not a silver bullet. There are other kinds of daabases and there can be rasons for not normalizing (parts of) tables. But anyway, here's an sscce using PDO:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'localonly', 'localonly');
//echo 'client version: ', $pdo->getAttribute(PDO::ATTR_CLIENT_VERSION), "\n";
//echo 'server version: ', $pdo->getAttribute(PDO::ATTR_SERVER_VERSION), "\n";
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
setup($pdo);
$stmt = $pdo->prepare("
SELECT
m.name
FROM
soFruits as f
JOIN
soMixXFruits as x
ON
f.id=x.idFruit
JOIN
soFruitMix as m
ON
x.idMix=m.id
WHERE
f.name=?
");
$stmt->setFetchMode(PDO::FETCH_ASSOC);
foo($stmt, 'apple');
foo($stmt, 'melon');
function foo(PDOStatement $stmt, $param) {
echo "----- $param -----\r\n";
$stmt->execute( array($param) );
foreach($stmt as $r) {
echo join(', ', $r), "\r\n";
}
}
function setup($pdo) {
$queries = array(
'
CREATE TEMPORARY TABLE soFruits (
id INT auto_increment,
name varchar(64),
primary key(id)
)
',
'
CREATE TEMPORARY TABLE soFruitMix (
id INT auto_increment,
name varchar(32),
primary key(id)
)
',
'
CREATE TEMPORARY TABLE soMixXFruits (
idMix int,
idFruit int,
primary key(idMix, idFruit)
)
',
"INSERT INTO soFruits (id,name) VALUES (1,'apple'),(2,'cherry'),(3,'grape'),(4,'kiwi'),(5,'lemon'),(6,'melon'),(7,'orange'),(8,'pear')",
"INSERT INTO soFruitMix (id,name) VALUES (1, 'mix1'),(2,'mix2'),(3,'mix3'),(4,'mix4')",
);
foreach( $queries as $q ) {
$pdo->exec($q);
}
$data = [
'1'=>['apple','orange','pear','grape','lemon'],
'2'=>['pear','melon','apple','kiwi','lemon'],
'3'=>['orange','kiwi','pear','apple','cherry'],
'4'=>['grape','lemon','cherry','apple','melon']
];
$stmt = $pdo->prepare('
INSERT INTO soMixXFruits (idMix, idFruit)
SELECT
:idMix, id
FROM
soFruits
WHERE
name=:nameFruit
');
$stmt->bindParam('idMix', $idMix);
$stmt->bindParam('nameFruit', $nameFruit);
foreach($data as $idMix=>$mix) {
foreach($mix as $nameFruit) {
$stmt->execute();
}
}
}
Take a look at the table definitions in function setup($pdo). You have entities like fruits and mixes and then there's a table that represents their relationship.
side-note: I didn't pay attention to the indicies. But for performance that is usually crucial, see http://www.sitepoint.com/using-explain-to-write-better-mysql-queries/
Related
I am building a project with an organization chart using Codeigniter + MySQL + Active Record.
There are departments listed as organization tree, Staff for information of the persons, Staff Roles and Staff_Departments where i store the matching:
Department - Staff - Role
You can see the structure below:
Departments (parent_id is used to build the tree)
Staff (raw Staff information)
Staff Roles (the lowest the weight, the highest in hierarchy)
Staff Departments (In which department - Who - What Role)
In a later phase, a Staff will probably belong in 2 or more departments with different roles. That's why i used a separate table Staff_departments for many-to-many. In this case, let's keep it simple and assume that 1 Staff belongs to 1 Department.
What i am trying to do:
A Manager(role weight=0 || role_id=1) in a Department, can view the Staff(Employees AND Supervisors) who work in his Department AND all the Staff(Employees AND Supervisors) from the Departments that are Children of his Department. The depth of the tree is unknown.
A Supervisor can view the Staff(only Employees) who work only in his Department.
An Employee can only view himself.
For Supervisors and Employees, the process is simple, so i think i am ok with that. For Managers probably i have to do something recursive, but i'm struggling every time i'm starting writing some lines of code.
My idea is to have a function find_related_staff($staff_id) {} in my controller, which i will pass the ID of the Staff who is logged in and it will return an array with the IDs of his related Staff. The only thing i got, is the ID of the Staff who is logged in.
If Manager return IDs of Managers,Supervisors and Employees related in his Department AND Managers,Supervisors and Employees from
Child Departments of his Department.
If Supervisor return IDs of Supervisors and Employees related in his Department only.
If Employee return his ID
Any idea on how to achieve that?
Ok, I think that, in order to make things easier to understand, we need to break your problem into small pieces (and I'm only focus on the section that you say you really need help: the managers' recursion).
First, we get the current department associated with the user authenticated. As you've said, you only have the ID of the staff currently sign so we'll start with that. Let's say the user id is assigned to variable $user_id.
$user_department = $this->db->get_where('staff_departments', ['staff_id' => $user_id])->row();
Now that we have the department, we check to see what's the role of the user in that department. We'll add that info to the $user_department object:
$user_department->role = $this->db->get_where('staff_roles', ['role_id' => $user_department->role_id])->row();
Let's check the weight of the user's role, shall we? If it's 0, we know it's a manager on that department so we'll recursively find the nested departments and their staff information. As per your logic dictates, we can check, here, if the user is a supervisor, also, and escalate if necessary. Like this:
if ($user_department->role->role_weight <= 1) {
// the user is a supervisor OR a manager, but both those can see, at least, the current department's staff information
$user_department->staff = $this->db->get_where('staff_departments', ['department_id' => $user_department->department_id]);
// now is the user a manager? If so, let's find nested departments
if ($user_department->role->role_weight === 0) {
$user_department->childs = $this->getChildDepartmentsAndStaffOf($user_department->department_id);
}
}
As you may note, there's a function that'll be called recursively. It must be something along this lines:
public function getChildDepartmentsAndStaffOf($department_id)
{
$child_departments = $this->db->get_where('departments', ['parent_id' => $department_id]);
if (! $child_departments) {
return null;
}
foreach ($child_departments as &$department) {
$department->staff = $this->db->get_where('staff_departments', ['department_id' => $department->department_id]);
$department->childs = $this->getChildDepartmentsAndStaffOf($department->department_id);
}
return $child_departments;
}
Now, you have the structure you want. I know this might be refactored, but I think that is enough to get your answer and point you to the right path.
Hope I've helped a little.
Yes, to get it done you must use recursive procedures. (I'm using MySQL 5.6.19)
I have created some test data before the stored procedures:
Sample data based on your question requirements:
create table departments
(
id int not null primary key auto_increment,
parent_id int,
department_name varchar(100)
);
insert into departments (id,parent_id,department_name)
values
(1,0,'Test A'),
(2,1,'Test B'),
(3,2,'Test C');
create table staff
(
id int not null primary key auto_increment,
ip_address varchar(100),
username varchar(100)
);
insert into staff values
(1,'127.0.0.1','ats'),
(2,'127.0.0.1','admin'),
(3,'127.0.0.1','george'),
(4,'127.0.0.1','jhon')
;
create table staff_roles
(
role_id int not null primary key auto_increment,
role_name varchar(100),
role_height int
);
insert into staff_roles values
(1,'Manager',0),
(2,'Supervisor',1),
(3,'Employee',2)
;
create table staff_departments
(
staff_department_id int not null primary key auto_increment,
department_id int,
staff_id int,
role_id int
);
insert into staff_departments values
(1,1,2,1),
(2,2,1,2),
(3,3,3,3),
(4,3,4,3);
It's time to create the stored procedures:
find_related_staff is the procedure that receives the staff_id parameter, according to that value will find the role_id in staff_departments table.
The variable #result will accumulate the final result as comma separated values.
find_recursive is the procedure that search in child departments and get the staff_id into #result variable;
The procedure code:
delimiter $$
drop procedure if exists find_related_staff$$
create procedure find_related_staff(p_id int)
begin
declare p_role_id int;
declare p_department_id int;
declare p_return varchar(255) default '';
declare p_role varchar(100);
select d.role_id, d.department_id, r.role_name
into p_role_id,p_department_id, p_role
from staff_departments d
inner join staff_roles r on d.role_id = r.role_id
where d.staff_id = p_id
limit 1;
case p_role_id
when 3 then -- employee (return the same id)
set #result = p_id;
when 2 then -- supervisor
select group_concat(s.staff_id)
into #result
from staff_departments s
where
s.role_id = 3
and s.department_id in
( select d.id
from departments d
where d.parent_id = p_department_id )
and s.role_id <> p_id;
when 1 then -- manager (complex recursive query)
select coalesce(group_concat(s.staff_id),'')
into #result
from staff_departments s
where
s.department_id = p_department_id
and s.staff_id <> p_id;
-- here we go!
call find_recursive(p_department_id);
end case;
select #result as result, p_role as role;
end $$
delimiter ;
delimiter $$
drop procedure if exists find_recursive$$
create procedure find_recursive(p_dept_id int)
begin
declare done int default false;
declare p_department int default false;
declare tmp_result varchar(255) default '';
-- cursor for all depend departments
declare c_departments cursor for
select s.department_id
from staff_departments s
where
s.department_id in
( select d.id
from departments d
where d.parent_id = p_dept_id );
declare continue handler for not found set done = true;
-- getting current departmens
set tmp_result =
(select coalesce(group_concat(s.staff_id),'')
from staff_departments s
where
s.department_id in
( select d.id
from departments d
where d.parent_id = p_dept_id ));
if length(tmp_result) > 0 then
if length(#result) > 0 then
set #result = concat(#result,',',tmp_result);
else
set #result = tmp_result;
end if;
open c_departments;
read_loop: loop
fetch c_departments into p_department;
if done then
leave read_loop;
end if;
call find_recursive(p_department);
end loop;
close c_departments;
end if;
end $$
delimiter ;
Testing:
Important: The max deep in recursion is 0 as default, we must change that value:
SET max_sp_recursion_depth=255;
Now we have the bellow configuration on your staff_departments table:
+---------------------+---------------+----------+---------+
| staff_department_id | department_id | staff_id | role_id |
+---------------------+---------------+----------+---------+
| 1 | 1 | 2 | 1 |
| 2 | 2 | 1 | 2 |
| 3 | 3 | 3 | 3 |
| 4 | 3 | 4 | 3 |
+---------------------+---------------+----------+---------+
Running each case:
call find_related_staff(2);
+--------+---------+
| result | role |
+--------+---------+
| 1,3,4 | Manager |
+--------+---------+
call find_related_staff(1);
+--------+------------+
| result | role |
+--------+------------+
| 3,4 | Supervisor |
+--------+------------+
call find_related_staff(3);
+--------+----------+
| result | role |
+--------+----------+
| 3 | Employee |
+--------+----------+
call find_related_staff(4);
+--------+----------+
| result | role |
+--------+----------+
| 4 | Employee |
+--------+----------+
Enjoy!
I think the most powerfull schema for hierarchical data in a relational database is the transitive-closure-table.
Given your sample data for the departments table:
department_id | parent_id | department_name
--------------|-----------|----------------
1 | 0 | TEST A
2 | 1 | TEST B
3 | 2 | TEST C
Your closure table (let's just call it departments_tree) would be like:
super_id | sub_id
---------|-------
1 | 1
1 | 2
1 | 3
2 | 2
2 | 3
3 | 3
Read it as: super_id = superordinate department_id; sub_id = subordinate department_id.
Assuming the logged-in user is manager of department with department_id = 2, the query to get all "supervised" employees is:
SELECT DISTINCT s.*
FROM departments_tree t
JOIN stuff_departments sd ON sd.department_id = t.sub_id
JOIN staff s ON s.id = sd.staff_id
WHERE t.super_id = 2
You can use triggers to populate and update the closure table.
Insert trigger:
DELIMITER //
CREATE TRIGGER `departments_after_insert` AFTER INSERT ON `departments` FOR EACH ROW BEGIN
INSERT INTO departments_tree (super_id, sub_id)
SELECT new.department_id, new.department_id
UNION ALL
SELECT super_id, new.department_id
FROM departments_tree
WHERE sub_id = new.parent_id;
END//
DELIMITER ;
Delete trigger:
DELIMITER //
CREATE TRIGGER `departments_before_delete` BEFORE DELETE ON `departments` FOR EACH ROW BEGIN
DELETE FROM departments_tree
WHERE sub_id = old.department_id;
END//
DELIMITER ;
Update trigger:
DELIMITER //
CREATE TRIGGER `departments_before_update` BEFORE UPDATE ON `departments` FOR EACH ROW BEGIN
DELETE t
FROM departments_tree p
CROSS JOIN departments_tree c
INNER JOIN departments_tree t
ON t.super_id = p.super_id
AND t.sub_id = c.sub_id
WHERE p.sub_id = old.parent_id
AND c.super_id = new.department_id;
INSERT INTO departments_tree (super_id, sub_id)
SELECT p.super_id, c.sub_id
FROM departments_tree p
CROSS JOIN departments_tree c
WHERE p.sub_id = new.parent_id
AND c.super_id = new.department_id;
END//
Note
You will not need a delete trigger, if you use foreighn keys with ON DELETE CASCADE:
CREATE TABLE `departments_tree` (
`super_id` INT(10) UNSIGNED NOT NULL,
`sub_id` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (`super_id`, `sub_id`),
INDEX `sub_id_super_id` (`sub_id`, `super_id`),
FOREIGN KEY (`super_id`) REFERENCES `departments` (`department_id`) ON DELETE CASCADE,
FOREIGN KEY (`sub_id`) REFERENCES `departments` (`department_id`) ON DELETE CASCADE
);
Note 2
In many implementations of a transitive closure table, you will find a depth or level column.
But you don't need it for the given requirements. And I believe you will never really need it,
as long as you don't try to format tree output in SQL.
I think your main problem is how to traverse down so that will be solved by getRecursiveDepts(). I have not completed the code but you can try something like this
file db.php
class DB {
private $servername = "127.0.0.1";
private $username = "root";
private $password = "root";
private $dbname = "test";
private $port = '3306';
public function getRecursiveDepts($deptIds) {
if (!is_array($deptIds)) {
$deptIds = array($deptIds);
}
$sql = "SELECT id FROM Departments WHERE parentId IN (";
$sql .= implode(', ', $deptIds);
$sql .= ")";
$conn = new mysqli($this->servername, $this->username, $this->password, $this->dbname, $this->port);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$newDept = array();
while($row = $result->fetch_assoc()) {
array_push($newDept, $row['id']);
}
$conn->close();
$moreDepts = $this->getRecursiveDepts($newDept);
if (is_null($moreDepts)) {
$finalIds = array_unique(array_merge($deptIds, $newDept));
} else {
$finalIds = array_unique(array_merge($deptIds, $newDept, $moreDepts));
}
return $finalIds;
} else {
$conn->close();
return null;
}
}
public function getRoles($empId) {
$sql = "SELECT role_id, department_id FROM staff_departmen_role WHERE staff_id = '$empId' GROUP BY role_id, department_id";
$conn = new mysqli($this->servername, $this->username, $this->password, $this->dbname, $this->port);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$emp = array();
while($row = $result->fetch_assoc()) {
if (!array_key_exists($row['role_id'], $emp)) {
$emp[$row['role_id']] = array();
}
array_push($emp[$row['role_id']], $row['department_id']);
}
}
$conn->close();
return $emp;
}
public function getEmpDetails($empId) {
$sql = "SELECT role_id, department_id FROM staff_departmen_role WHERE staff_id = '$empId' GROUP BY role_id, department_id";
$conn = new mysqli($this->servername, $this->username, $this->password, $this->dbname, $this->port);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$emp = array();
while($row = $result->fetch_assoc()) {
if (!array_key_exists($row['role_id'], $emp)) {
$emp[$row['role_id']] = array();
}
array_push($emp[$row['role_id']], $row['department_id']);
}
}
$conn->close();
return $emp;
}
}
file index.php
<?php
include_once 'db.php';
$objDB = new DB();
$empId = 2;
$emps = $objDB->getRoles($empId);
foreach ($emps as $roleId => $deptIds) {
switch ($roleId) {
case 1:
$allDeptIds = $objDB->getRecursiveDepts($deptIds);
break;
case 2://Supervisor GetEmpIds of current dept role >= 2
break;
case 3://Employee GetEmpIds of current dept role >= 2
$emp = $objDB->getEmpDetails($empId);
break;
default:
# code...
break;
}
}
$data = $objDB->getRecursiveDepts($empId);
print_r($data);
?>
I am doing a recipe book. My database tables look like this:
Table "recipes" has columns: id, name
Table "ingredients" has columns: id, name, measurement_id
Table "recipes_have_ingredients" has columns: id, recipe_id, ingredient_id, ingredient_amount, unit_id
Table "units" has columns: id, name, measurement_id
The idea is that the user will be able to insert an ingredient for a recipe selecting how it is measured (volume, mass, length...), and then, it will display a list of the units for that measurement; for example, if I insert chicken, and I select it as "mass", next time the ingredient chicken is introduced, it will display a list of the units for measuring mass.
Anyway..... at the moment, I am trying to display a table with the recipe name, and the ingredients list, with the ingredient name, amount and unit name.
If I do the query in mysql as
SELECT recipes_have_ingredients.recipe_id
, ingredients.name
, recipes_have_ingredients.ingredient_amount
, units.name
FROM recipes_have_ingredients
JOIN ingredients
ON recipes_have_ingredients.ingredient_id = ingredients.id
JOIN units
ON recipes_have_ingredients.unit_id = units.id
WHERE recipe_id = 2
it displays:
+-----------+-------+-------------------+----------+
| recipe_id | name | ingredient_amount | name |
+-----------+-------+-------------------+----------+
| 2 | onion | 1 | kilogram |
| 2 | milk | 30 | litre |
+-----------+-------+-------------------+----------+
Which is what I want, but, if I try to create a function of it as:
public function display_ingredients_for_recipe($recipe_id = ':recipe_id') {
include 'includes/db_connection.php';
try {
$sql = "SELECT recipes_have_ingredients.recipe_id, ingredients.name, recipes_have_ingredients.ingredient_amount, units.name FROM recipes_have_ingredients JOIN ingredients ON recipes_have_ingredients.ingredient_id=ingredients.id JOIN units ON recipes_have_ingredients.unit_id=units.id WHERE recipe_id=:recipe_id";
$results = $conn->prepare($sql);
$results->bindParam(':recipe_id', $recipe_id, PDO::PARAM_INT);
$results->execute();
} catch(PDOException $e) {
echo 'Error: ' . $e->getMessage() . '<br />';
return array();
}
return $results->fetchAll(PDO::FETCH_ASSOC);
}
And I try to use it (recipe['id']) is coming from the display_recipes method:
public function display_recipes() {
include 'includes/db_connection.php';
try {
$sql = "SELECT id, name FROM recipes";
$results = $conn->prepare($sql);
$results->execute();
} catch (PDOException $e) {
echo 'Error: ' . $e->getMessage() . '<br />';
return array();
}
return $results->fetchAll(PDO::FETCH_ASSOC);
}
$ingredients = $recipeObj->display_ingredients_for_recipe($recipe['id']);
echo '<pre>';
print_r($ingredients);
echo '</pre>'; die();
It is displaying:
Array
(
[0] => Array
(
[recipe_id] => 1
[name] => gram
[ingredient_amount] => 350
)
)
And I don't know what I am doing wrong.... why if I do the query on mysql it selects what I want but then, if I try to debug the ingredients I can't make the ingredient name and the unit name to appear.
Any help will be appreciated.
Because you have two fields with the same name (which, ironically, also happens to be "name"). PHP can't have two of the same key in an array, so they overwrite. If you rename one (or both) of the fields, it should work.
SELECT recipes_have_ingredients.recipe_id, ingredients.name AS ingredient_name, recipes_have_ingredients.ingredient_amount, units.name AS unit_name ...
I have a query in mySQL
SELECT id FROM admin_products;
which return a list of ids, like so
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+------+
And I was using PHP to dynamically generate tables like
vendor_1, vendor_2, vendor_3, vendor_4, vendor_5
Now I want to write a query to retrieve the price and quantity from the table id
For example
"ENTER QUERY HERE"
Should retrieve
+-----------------------------+
| id | price | quantity |
+-----------------------------+
| 1 | 23| 13| // price and quantity retrieved from table vendor_1 since id=1
| 2 | 158| 85| // price and quantity retrieved from table vendor_2 since id=2
| 3 | 15| 7| // price and quantity retrieved from table vendor_3 since id=3
| 4 | 112| 9| // price and quantity retrieved from table vendor_4 since id=4
| 5 | 123| 199| // price and quantity retrieved from table vendor_5 since id=5
+-----------------------------+
What I'm doing now in PHP is:
$conn = mysqli_connect($server,$user,$pwd,$db);
$sql = "SELECT id FROM admin_products";
$res = mysqli_query($conn,$sql);
if(mysqli_num_rows($res)>0){
while($row = mysqli_fetch_assoc($res)){
$product = array();
$innerSQL = "SELECT price,quantity FROM vendor_".$row['id'];
$innerRes = mysqli_query($conn,$innerSQL);
if(mysqli_num_rows($innerRes)>0){
while($innerRow = mysqli_fetch_assoc($innerRes)){
array_push($product,$row['id']);
array_push($product,$innerRow['price']);
array_push($product,$innerRow['quantity']);
}
}
}
}
But it takes two hits to the mySQL database. Can't it be reduced to one?
EDIT
I have later on realized that my database structure was incorrect and dynamically creating tables is a very bad idea and could spell disaster later on
-Solution 1:
Note: This will only work if you have in your vendor_x tables id for the vendor id to match them with. (As Strawberry said, this is a terrible idea to dynamically generate tables).
After selecting the correct id you can do something like this:
connect to the MySql Server
Then you can create the table name and store it in a variable.
$tableName = 'vendor_' . $id;
I would suggest after that to have a check if the table exists with a simple query:
$sql = "SHOW TABLES LIKE '$tableName'";
If this returns empty result you can throw an exception that the table does not exist or handle it whatsoever way you would like.
After checking every table, to be sure it exists, you can create your query.
$joins = "";
$sql = "
SELECT
v.id,
price,
quantity
FROM
vendors AS v
";
foreach ($ids as $id) {
$tableName = "vendor_" . $id;
$tableAlias = "v".$id;
$joins .= " LEFT JOIN " . $tableName . " AS ". $tableAlias ."
ON (v.id = ". $tableAlias .".vendor_id) ";
}
$sql .= $joins;
Then execute the query.
-Solution 2:
Create only one table to manage your vendors. It should have a structure like this :
`id` // AI value
`vendor_id` // The id of the vendor to easily join it afterwards
`price`
`quantity`
You can name it something like vendor_product or whatsoever
And now you have only one simple query:
$sql = "
SELECT
v.id,
vp.quantity,
vp.price
FROM
vendors AS v
LEFT JOIN vendor_product AS vp
ON (vp.vendor_id = v.id)
";
EDIT for the comment about the structure:
You will need one table for the vendors, such so:
`vendor`:
`id`, //AI value
`username`,
`password` // I suggest to you not to keep it in plain text.
`vendor_product` :
`id`, //AI value
`vendor_id`,
`price`,
`quantity`
I don't know here if you are going to store more information about each product, but this should do the trick.
How to show the product with least price ?
You need to match them by somehow and group by that selecting minimum price.
Try this if it suits
$table = "vendor"."_".$id; // this will create table name if $id = 1 then $table = vendor_1;
mysqli_query($connect , "SELECT * FROM $table");
2nd
If you want to fetch data of all table at once then
1) fetch id from admin_products and store in an array like
$ids = array(1,2,3,4,5);
2) Now loop throw array and create sql;
$sql = "SELECT * FROM ";
$ids = array(1,2,3,4,5);
foreach($ids as $id){
$table = "vendor"."_".$id; // this will create table name if $id = 1 then $table = vendor_1;
$sql .=" $table,";
}
$sql = rtrim($sql,",");// this will trim the last comma
echo $sql;
// output SELECT * FROM vendor_1, vendor_2, vendor_3, vendor_4, vendor_5
I have a database (mysql) table like
id | party | rights | m_id
---+---------+------------+---------
9 |abc | 3,5,6 | ["12,","15,"6"]
20 |xyz | 5,2 | ["6,","2,"9","12"]
21 |xyz 1 | 5,2 | ["6,","9,"12"]
Now I want to make my table in this way
search result for rights 5 is ["12,","15,"6"] ["6,","2,"12"] ["6,","9,"12"]
12 | abc , xyz,xyz1 |
15 | abc|
6 | abc , xyz,xyz1 |
9 | xyz,xyz1 |
Let's start with what I believe you already have. This is an sscce. If you adjust the mysql credentials it should run on your system, creating only a temporary MySQL table. It uses PDO to access the MySQL server. Which API you actually use is not important (i.e. as long as the other API is mysqli, because the mysql_* functions are depreacted ;-))
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'localonly', 'localonly', array(
PDO::ATTR_EMULATE_PREPARES=>false,
PDO::MYSQL_ATTR_DIRECT_QUERY=>false,
PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION
));
setup($pdo);
$statement = $pdo->prepare('
SELECT
*
FROM
soFoo
WHERE
FIND_IN_SET(:right, rights)
');
$statement->execute( array(':right'=>5) );
/* in one way or another you have a loop where you fetch the records
having '5' in the `rights` field
*/
foreach( $statement as $row ) {
echo $row['party'], ' ', $row['show_ids'], "\r\n";
}
function setup($pdo) {
$pdo->exec('
CREATE TEMPORARY TABLE soFoo
(`id` int, `party` varchar(9), `exc` varchar(13), `rights` varchar(5), `show_ids` varchar(27))
');
$pdo->exec( <<< eos
INSERT INTO soFoo
(`id`, `party`, `exc`, `rights`, `show_ids`)
VALUES
(9, 'Percept', 'Non-Exclusive', '3,5,6', '["12,","15,"6"]'),
(20, 'Tata Sky', 'Non-Exclusive', '5,4,', '["6,","9,"11"]'),
(21, 'Tata Sky', 'Exclusive', '5,4', '["6,","13","15,"2","4","9"]'),
(22, 'Simbiotic', 'Exclusive', '6,2', '["12,","15,"1","6","7","8"]')
eos
);
}
this prints
Percept ["12,","15,"6"]
Tata Sky ["6,","9,"11"]
Tata Sky ["6,","13","15,"2","4","9"]
and is (as I understand the question) as far as you've already got.
Now let's decode the JSON array and check whether it contains the element 9. If it does add inforamtion from the current row to an array called $parties
$parties = array();
/* in one way or another you have a loop where you fetch the records
having '5' in the `rights` field
*/
foreach( $statement as $row ) {
$ids = json_decode($row['show_ids'], true);
if ( in_array('9', $ids) ) {
$parties[$row['id']] = $row['party'];
}
}
var_export($parties);
prints
array (
20 => 'Tata Sky',
21 => 'Tata Sky',
)
But ... from a relational database point of view this is ....suboptimal.
The FIND_IN_SET clause hinders MySQL from using indices effectively; you're searching (compound) data within a single field. It's amazing what the database server implementations can do to improve performance; but it has limits.
And you're also transfering possibly unnecessary data from the MySQL server to the php instance (those records that have 5 in rights but not 9 in show_ids). If possible, this should be avoided. Networks/Network stacks are fast and can be optimized, RAM is cheap ...but again, there are limits.
So, I suggest you look into Database normalization on the one hand and/or document-oriented databases on the other hand.
I am going to give a example below so you can write in your way,
If you are having table below
id | name
1 | a,b,c
2 | b
and expected output like this
id | name
1 | a
1 | b
1 | c
2 | b
Then follow below solutions
If you can create a numbers table, that contains numbers from 1 to the maximum fields to split, you could use a solution like this:
select
tablename.id,
SUBSTRING_INDEX(SUBSTRING_INDEX(tablename.name, ',', numbers.n), ',', -1) name
from
numbers inner join tablename
on CHAR_LENGTH(tablename.name)
-CHAR_LENGTH(REPLACE(tablename.name, ',', ''))>=numbers.n-1
order by
id, n
Please see fiddle here.
If you cannot create a table, then a solution can be this:
select
tablename.id,
SUBSTRING_INDEX(SUBSTRING_INDEX(tablename.name, ',', numbers.n), ',', -1) name
from
(select 1 n union all
select 2 union all select 3 union all
select 4 union all select 5) numbers INNER JOIN tablename
on CHAR_LENGTH(tablename.name)
-CHAR_LENGTH(REPLACE(tablename.name, ',', ''))>=numbers.n-1
order by
id, n
an example fiddle is here.
this is my table
row | car_id | car_model | car_features |
1 1 CAR 1 Features 1
2 2 CAR 2 Features 2
3 2 CAR 2 Features 3
and i want to make it like
row | car_id | car_model | car_features |
1 1 CAR 1 Features 1
2 2 CAR 2 Features 2, Features 3
and this is my php mysql script:
<?php
$con = mysql_connect("localhost", "root", "root");
mysql_select_db("car", $con);
$format = mysql_query("SELECT c.* , p.*, d.*,f.* ,e.* FROM bsi_car_master c,bsi_car_type p, bsi_car_vendor d, bsi_selected_features f, bsi_car_features e WHERE c.car_type_id=p.id AND c.car_vendor_id=d.id AND c.car_id = f.car_id AND f.features_id = e.id");
$row = 1;
while($srow = mysql_fetch_array($format))
{
blah blah blah....
}
?>
Use GROUP_CONCAT with GROUP BY. Try this -
SELECT `row`, `car_id`, `car_model`, GROUP_CONCAT(`car_features`, ',')
FROM your_table GROUP BY `car_id`
SELECT car_id,car_model,GROUP_CONCAT(car_features,',')
FROM yourtable
GROUP BY car_id,car_model;
$format = mysql_query("SELECT c.* , p.*, d.*,f.* ,e.*,group_concat(`c.car_features`,',') as `carfeatures` FROM bsi_car_master c,bsi_car_type p, bsi_car_vendor d, bsi_selected_features f, bsi_car_features e WHERE c.car_type_id=p.id AND c.car_vendor_id=d.id AND c.car_id = f.car_id AND f.features_id = e.id group by c.car_id");
Hopefully, it's fairly obvious that I'm no PHP coder, but here's another way to do it, without GROUP_CONCAT()...
<?php
/*
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,car_id INT NOT NULL
,car_model VARCHAR(12) NOT NULL
,car_features VARCHAR(20) NOT NULL
);
INSERT INTO my_table VALUES
(1 ,1 ,'CAR 1','Features 1'),
(2 ,2 ,'CAR 2','Features 2'),
(3 ,2 ,'CAR 2','Features 3');
*/
require('path/to/mysqli/connection/stateme.nts');
$query = "
SELECT id
, car_id
, car_model
, car_features
FROM my_table
ORDER
BY car_model;
";
$result = mysqli_query($db,$query);
$car_id = 0;
while($row = mysqli_fetch_assoc($result)){
if ($car_id== $row['car_id']){
echo " >".$row['car_features']."<br>\n";
} else {
echo $row['car_model']."<br>\n >".$row['car_features']."<br>\n";
$car_id = $row['car_id'];
}
} // end of while loop
/*
Outputs...
CAR 1
>Features 1
CAR 2
>Features 2
>Features 3
*/
?>