I want to insert multiple row in my table, the data for this query is fetched from front end. One column in this query is not passed from front end, its value is determined at query execution time from another table. I can put these array in loop & perform one SELECT query & one INSERT query for each item, but I want to avoid it, thus I created one large INSERT query! Till here everything fine, but now that one value should be from another table!
I can use INSERT...SELECT method
https://dev.mysql.com/doc/refman/5.6/en/insert-select.html
It allows insertion of multiple rows, all rows matched are inserted & I can pass a dummy column value in SELECT part to customise the query. But now problem is these 'faked columns' are same for all rows, they should me in loop, different for each loop! how to achieve that?
Scenario
$products = array(
"1" => array(
"product_name" => "apple",
"units" => "1",
),
"2" => array(
"product_name" => "mango",
"units" => "3",
),
);
Suppose this is the array I get from front end, each key is product id which contains other description for product in cart. Now I'll insert this in Orders table but price is missing, which I have to fetch from Products table! For which I can perform select using product id.
Similar Question:
This answer uses faked columns:
MYSQL INSERT SELECT problem
Copied from accepted answer:
INSERT INTO website (url,fid) SELECT $site,id FROM users WHERE name = $user
Here $site will be same for all inserted records, I want it different for each record as per my array!
I research on this topic but can't find desired answer :(
Try this code if it helps,
<?php
$products = array(
"1" => array(
"product_name" => "apple",
"units" => "1",
),
"3" => array(
"product_name" => "mango",
"units" => "3",
),
"7" => array(
"product_name" => "mango",
"units" => "3",
),
);
$result = $conn->query("SELECT product_id, product_price FROM product_table WHERE product_id IN (".implode(',', array_keys($products)).")");
while($row = $result->fetch_assoc()) {
$prod_price[$row['product_id']] = $row['product_price'];
}
$qry = array();
foreach ($products as $key => $value) {
$qry[] = "('".$value['product_name']."', '".$value['units']."', '".$prod_price[ $key]."')";
}
$insert = "INSERT INTO orders (`product_name`, `units`, `product_price`) VALUES ".implode(', ', $qry);
I come up with this code based on my understanding of your question. Let me know if it works.
This approach just hits the DB twice only.
Related
I am trying to do a mysql sort that displays 0 first and then by the smallest number.
$query = "SELECT DISTINCT id FROM `items` WHERE `name`='Mag' AND `var`='Bl' ORDER BY atrow + 0 ASC"
How to write it in medoo?
$item = $database->select("items", "#id", [
"var[=]" => "Bl",
"name[=]" => "Mag",
"ORDER" => ["atrow" => "ASC"]
]);
This is not working properly.
You need a two tiered sort here. Assuming we use the following raw MySQL query:
SELECT DISTINCT id
FROM item
ORDER BY row != 0, row;
PHP code:
$item= $database->select("items", "#id", [ "ORDER" => Medoo::raw("`row` != 0, `row`")]);
Writing in PHP, I have 2 arrays, each created from SQL queries.
The first query runs through a table that has multiple pieces of data that correspond to various quiz attempts. The table has a column for the user's Email, the activity ID (which represents a quiz attempt) and another 2 columns for data relating to the attempt (for example 'percentage achieved' or 'quiz ID'):
UserEmail ActID ActKey ActMeta
joB#gm.com 2354 Percentage 98
joB#gm.com 2354 Quiz ID 4
boM#hm.com 4567 Percentage 65
boM#hm.com 4567 Quiz ID 7
Once queried, this first array ($student_quiz_list) stores the selected data in the form of
[[UserEmail, ActID, ActKey, ActMeta], [UserEmail, ActID, ActKey, ActMeta], [UserEmail, ActID, ActKey, ActMeta]...]
where each pair of sub-arrays corresponds to a single quiz attempt.
The second table that is queried has two columns that relate to the quizzes themselves. The first column is the Quiz ID and the second is the Quiz name.
Quiz ID Quiz Name
4 Hardware
7 Logic
Once queried, this second array ($quiz_list) stores the selected data in the form of
[[ID, Name], [ID, Name]...]
What I need to do is create a 3rd array (from the 2 above) which holds the user's email and percentage score
[email, percentage], [email, percentage]...]
but with each sub-array corresponding to a unique actID (so basically the user's percentage in each quiz they attempted without duplicates) and (this is the challenging bit) only for quizzes with certain ID values, in this case, let's say quiz ID 4.
In PHP, what would be the most efficient solution to this? I continually create arrays with duplicates and cannot find a neat solution which provides the outcome desired.
Any help would be greatly received.
Try this code as the example and let me know.
$student_quiz_list=array(
array(
'UserEmail'=>'joB#gm.com','ActID'=>'2354','ActKey'=>'Percentage','ActMeta'=>'90',
),
array(
'UserEmail'=>'joB#gm.com','ActID'=>'2354','ActKey'=>'QuizID','ActMeta'=>'4',
),
array(
'UserEmail'=>'boM#hm.com','ActID'=>'4567','ActKey'=>'Percentage','ActMeta'=>'98',
),
array(
'UserEmail'=>'boM#hm.com','ActID'=>'4567','ActKey'=>'QuizID','ActMeta'=>'7',
),
);
$final_array=array();
foreach( $student_quiz_list as $row){
if($row['ActKey']=='Percentage'){
$final_array[]=array('UserEmail'=>$row['UserEmail'],
'ActMeta'=>$row['ActMeta']
) ;
}
}
echo"<pre>"; print_r($final_array); echo"</pre>";
As commenter #Nico Haase suggested, you can do most of the logic in SQL. You didn't respond to my comment, so I suppose a user can have multiple attempts per quiz ID:
SELECT
UserEmail,
ActMeta
FROM
your_table # replace with your table name
WHERE
ActKey = 'Percentage'
AND ActID IN (
# subselection with table alias
SELECT
t2.ActID
FROM
your_table t2 # replace with your table name
WHERE
t2.ActKey = 'Quiz ID'
AND t2.ActMeta = 2 # insert your desired quiz ID here
AND t2.ActID = ActID
)
(Query tested with MySQL/MariaDB)
For the case that you cannot change the SQL part, here is how you can process your data in PHP. But consider that a large dataset could exceed your server capabilities, so I would definitely recommend the solution above:
// Your sample data
$raw = [
['UserEmail' => 'joB#gm.com', 'ActID' => 2354, 'ActKey' => 'Percentage' , 'ActMeta' => 98],
['UserEmail' => 'joB#gm.com', 'ActID' => 2354, 'ActKey' => 'Quiz ID', 'ActMeta' => 4],
['UserEmail' => 'joB#gm.com', 'ActID' => 4567, 'ActKey' => 'Percentage' , 'ActMeta' => 65],
['UserEmail' => 'joB#gm.com', 'ActID' => 4567, 'ActKey' => 'Quiz ID', 'ActMeta' => 7],
];
// Extract the corresponding ActIDs for a QuizID
$quiz_id = 4;
$act_ids = array_column(
array_filter(
$raw,
function($item) use ($quiz_id) {
return $item['ActMeta'] == $quiz_id;
}
),
'ActID'
);
// Get the entries with ActKey 'Percentage' and an ActID present in the previously extracted set
$percentage_entries = array_filter(
$raw,
function($item) use ($act_ids) {
return $item['ActKey'] === 'Percentage' && in_array($item['ActID'], $act_ids);
}
);
// Map over the previous set to get the array into the final form
$final = array_map(
function($item) {
return [$item['UserEmail'], $item['ActMeta']];
},
$percentage_entries
);
I am trying to LIMIT a Query which is already been selected. I know I can directly do it in my query select, but due to some logics of the code, if I do that way I have to run the query selection twice which (I believe) will increase the computational time!
So, what I am trying is this:
$query1 = $mysqli->query("SELECT * FROM tableName WHERE someconditions");
then, I need to re-select a sub-selection of the $query1 for my Pagination. What I am trying to do is something like this;
$query2 = LIMIT($query1, $limit_bigin, $limit_end);
where $limit_bigin, $limit_end provide the LIMITING range (start and end respectively).
Could someone please let me know how I could do this?
P.S. I know I can do it directly by:
$query1 = $mysqli->query("SELECT * FROM tableName WHERE someconditions");
$query2 = $mysqli->query("SELECT * FROM tableName WHERE someConditions LIMIT $limit_bigin, $limit_end");
But this is running the query twice and slows down the process (I must run the first query without limits due to some logics of the program)
EDIT 1
As per the answers I tried using array_slice in PHP. BUT, since Query is an object it doesn't give the results that was expected. A NULL is resulted form
array_slice($query1, $start, $length, FALSE)
If you have already carried out the query and the result set has been returned to your PHP, you can not then LIMIT it. As you state, then running a second SQL execution of a subpart of the same query is wasteful and repetative.
Don't
Repeat
Yourself.
DRY.
As I said above, repetition causes issues with maintainability as you can easily forget about repetition, tweaking one SQL and forgetting to tweak the other.
Don't
Repeat
Yourself.
DRY.
Use PHP instead
Once you have executed the query, the result set is then passed back to PHP.
So assuming you have a $var with the contents of you SQL query, then you simply need to select the valid rows from the $var, not from the database [again].
You can do this using PHP numerous Array functions. Particularly array_slice().
So;
$query1 = $mysqli->query("SELECT * FROM tableName WHERE someconditions");
Now, to select the second page, say for example rows 10 to 20:
$query2 = array_slice($query1, (10-1), 10 );
This wil then "slice" the part of the array you want. Remember that the array counts will start at zero so to grab row 10 (of an index starting at 1, Typical of a MySQL Auto Increment Primary Key), then you will need to select X number of rows from row (10-1) .
Please also read the manual for PHP array_slice().
Further
As referenced in comments, there is no guarentee that your SQL will return the same values each time in the same order, so it is highly recommended to order your results:
$query1 = $mysqli->query("SELECT * FROM tableName
WHERE someconditions ORDER BY primary_key_column");
Example Data:
$query1 = $mysqli->query("SELECT * FROM tableName WHERE someconditions ORDER BY id");
/***
$query1 = array {
0 => array { 'id' => 1, 'user' => "Jim", 'colour' => "Blue" },
1 => array { 'id' => 2, 'user' => "Bob", 'colour' => "Green" },
2 => array { 'id' => 3, 'user' => "Tom", 'colour' => "Orange" },
3 => array { 'id' => 4, 'user' => "Tim", 'colour' => "Yellow" },
4 => array { 'id' => 5, 'user' => "Lee", 'colour' => "Red" },
5 => array { 'id' => 6, 'user' => "Amy", 'colour' => "Black" }
}
***/
$page = 2;
$size = 3; // number per page.
$start = ($page - 1) * $size; //page number x number per page.
// Select the second page of 3 results.
$query2 = array_slice($query1, $start, $size , FALSE);
/***
$query2 = array {
0 => array { 'id' => 4, 'user' => "Tim", 'colour' => "Yellow" },
1 => array { 'id' => 5, 'user' => "Lee", 'colour' => "Red" },
2 => array { 'id' => 6, 'user' => "Amy", 'colour' => "Black" }
}
***/
You can then use these in a foreach or other standard array manipulation technique.
So, I want to have table with users name, grades and subjects. Table will display only his grades. So I'm generating subject in foreach loop and reading grades depending on his id.
For subject I want to have an array which will contain infos about subject (teacher, classroom, etc.)
For now I have this array:
$subjects = array();
$getSubjects = mysqli_query($con, "SELECT * FROM predmeti");
while ($subject = mysqli_fetch_array($getSubjects)) {
$subjects[]= array(
$subject['subject_name'] => array(
'id' => $subject['id'],
'name' => $subject['name'],
'teacher' => $subject['teacher'],
'short_name' => $subject['short_name'],
'classroom' => $subject['classroom']
)
);
I know this isn't right. I can't get data for each subject.
Could you please help me?
You're accessing the columns by name while mysqli_fetch_array() returns only an integer indexed array. Have you tried mysqli_fetch_assoc()?
I am creating a questionnaire for a client that requires the questions to be organized by 3 layers of levels. I've successfully created the U.I. however I've been trying for the last 3 hours to pull data from a database in such a way that everything loads in the right place. The database is organized like so by the client so i have no control over it:
id description parentId about
1 Level 1 0 This is the top level or in my case tab1
2 Level 2 0 This is the next tab in the top level
3 Level 1a 1 This is the first category under tab1
4 Level 1b 1 This is the next category under tab1
5 Level 1a1 3 This is the content under the first category of tab1
So anything with a parentId of 0 is the top level and will contain anything of the second level with the parentId of 1 and so on. Confusing yes, I can barely make sense of this but this is how I've been told to do it.
What approach would be the best way to execute something like this? An example from another question I'm using as a reference is attached below (although not working)
foreach (mysql_query("SELECT * FROM pB_test ORDER BY id ASC") as $row) {
$menuitem = array_merge(array(), $row);
$menuLookup[$menuitem['id']] = $menuitem;
if ($menuitem['parent'] == null) {
$menuitem['path'] = "/" . $menuitem['name'];
$menu[] = $menuitem[];
} else {
$parent = $menuLookup[$menuitem['parent']];
$menuitem['path'] = $parent['path'] . "/" . $menuitem['name'];
$parent['menu'][] = $menuitem;
}
}
Any help would be greatly appreciated. Cheers
If you have exactly 3 levels, then you can try this:
http://sqlfiddle.com/#!2/70e96/16
(
SELECT 1 AS lvl,
top_level.description AS o1, top_level.id AS id1,
NULL AS o2, NULL AS id2,
NULL AS o3, NULL AS id3,
top_level.*
FROM node AS top_level
WHERE top_level.parentId = 0
)UNION ALL(
SELECT 2 AS lvl,
top_level.description AS o1, top_level.id AS id1,
category_level.description AS o2, category_level.id AS id2,
NULL AS o3, NULL AS id3,
category_level.*
FROM node AS top_level
INNER JOIN node AS category_level ON category_level.parentId = top_level.id
WHERE top_level.parentId = 0
)UNION ALL(
SELECT 3 AS lvl,
top_level.description AS o1, top_level.id AS id1,
category_level.description AS o2, category_level.id AS id2,
last_level.description AS o3, last_level.id AS id3,
last_level.*
FROM node AS top_level
INNER JOIN node AS category_level ON category_level.parentId = top_level.id
INNER JOIN node AS last_level ON last_level.parentId = category_level.id
WHERE top_level.parentId = 0
)
ORDER BY o1,o2,o3;
I added a lvl field to the selects, different value for each level. Also added o1,o2,o3 for ordering nested levels nicely, of course you may have another needs. You could process all rows in PHP, for example split them into 3 arrays (one for each level), or maybe create a lookup table by id, etc.
It might be worth doing this in PHP, as opposed to SQL if you're working with an external database. I haven't benchmarked the following, so try with your data and see if performance is problematic or not.
You can choose yourself what to do with orphaned records (which reference parentIDs that don't exist anymore).
Ordering in PHP like this requires that you have all of your data beforehand, so use something like PDO's fetchAll(PDO::FETCH_ASSOC) method, which should result in something like this:
$data_from_database = array(
array("id" => 1, "parentId" => 0, "description" => "Level 1"),
array("id" => 2, "parentId" => 1, "description" => "Level 1a"),
array("id" => 3, "parentId" => 1, "description" => "Level 1b"),
array("id" => 4, "parentId" => 0, "description" => "Level 2"),
array("id" => 5, "parentId" => 2, "description" => "Level 1a1"),
array("id" => 6, "parentId" => 5, "description" => "Level 1a11a"),
array("id" => 7, "parentId" => 5, "description" => "Level 1a11b"),
array("id" => 8, "parentId" => 9, "description" => "Level 3"),
);
First off, you'll want to have the primary key (ID) as the array's keys. The following also adds the keys "children" and "is_orphan" to every record.
$data_by_id = array();
foreach($data_from_database as $row)
$data_by_id[$row["id"]] = $row + array(
"children" => array(),
"is_orphan" => false
);
This will look something like this:
$data_from_database = array(
1 => array("id" => 1, "parentId" => 0, "description" => "Level 1",
"children" => array(), "is_orphan" => false),
...
);
Now, it gets tricky: we'll loop through the array and add references.
foreach($data_by_id as &$row)
{
if($row["parentId"] > 0)
{
if(isset($data_by_id[$row["parentId"]]))
$data_by_id[$row["parentId"]]["children"][] = &$row;
else
$row["is_orphan"] = true;
}
}
unset($row); // Clear reference (important).
The last step is to clean up the 'root' of the array. It'll contain references to duplicate rows.
foreach($data_by_id as $id => $row)
{
// If you use this option, you'll remove
// orphaned records.
#if($row["parentId"] > 0)
# unset($data_by_id[$id]);
// Use this to keep orphans:
if($row["parentId"] > 0 AND !$row["is_orphan"])
unset($data_by_id[$id]);
}
Use print_r($data_by_id) after every step to see what happens.
If this proves to be a time consuming operation, try to build up the tree by only doing SELECT id, parentId FROM ... and then later fetching the metadata such as description. You could also store the result in Memcache or serialized into a database.
i also had the same kind of problem but after lot of googling and stackoverflowing :-)
i found my answer....
Here is my way of coding.
function showComments($parent = 0)
{
$commentQuery = "SELECT * FROM comment WHERE parent = ".mysql_real_escape_string($parentId);
$commentResult = mysql_query($commentQuery)
while ($row = mysql_fetch_array($commentResult))
{
echo '[Table containing comment data]';
showComments($row['commentID']);
}
}
showComments();