I have this code that will perform an insert to the question, choice and multichoice table. I managed to accomplish in inserting question and choice table, what I failed to do is the multichoice table.
require("dbOption/Db.class.php");
$question = new Db();
$choice = new Db();
$multichoice = new Db();
$entries = array(
0 => '1. What foo?',
1 => 'a. foo1',
2 => 'b. foo2',
3 => 'c. foo3',
4 => 'd. foo4',
5 => '2. 2 + 2 is?',
6 => 'a. test1',
7 => 'b. test2',
8 => 'c. test3',
9 => 'd. test4',
);
$answerIDs = "";
$questionID = "";
$multipleChoice = array();
foreach ($entries as $entry) {
if(is_numeric(substr($entry, 0, 1)) === true) {
echo "<pre>";
var_dump($entry);
echo "<pre>";
$question->query("INSERT INTO question(q_name) VALUES(:question)",array("question"=>$entry));
$questionID = $question->lastInsertId();
} else {
echo "<pre>";
var_dump($entry);
echo "<pre>";
$answer->query("INSERT INTO choice(choices,question) VALUES(:choices, :question)",array("choices"=>$entry, "question"=>$questionID));
if ($answerIDs === "")
$answerIDs = $choice->lastInsertId();
else
// store last inserted ids in choice table and separate it with ","
$answerIDs .= ("," . $choice->lastInsertId());
}
}
This is the sample output in the db.
question table
id q_name
1 1. What foo?
2 2. 2 + 2 is?
choice table
id choices question correct
1 a. foo1 1 0
2 b. foo2 1 0
3 c. foo3 1 0
4 d. foo4 1 0
5 a. test1 2 0
6 b. test2 2 0
7 c. test3 2 0
8 d. test4 2 0
question in choice table is the id from question table
What I want to achieve in multichoice table.
multichoice table
id question mc_answers
1 1 1,2,3,4
2 2 5,6,7,8
question in multichoice table is the id from question table
I’m confused on how to do this, I would like to consult it to you guys. What should I do to achieve this?
Like I said, I don't think you should store those lists at all. Storing comma separated values is considered bad design in general, and besides you already have all the information you need in the database, so that information would be redundant too.
If you want to do this anyway, I think there are four solutions.
1) Insert the answers when you start a new question. Disadvantage is that you have to do the same thing again after the loop to save the choices of the last question. Structure of the code would look like this (stripped down for brevity).
$answerIDs = "";
foreach ($entries as $entry) {
if(is_numeric(substr($entry, 0, 1)) === true) {
if ($answerIDs !== "") {
// First insert multi select answers
// Reset the answers for the new question.
$answerIDs = "";
}
// Then insert question as you do now.
} else {
// Insert answers and store ID in $answerIDs as you do now.
}
}
if ($answerIDs !== "") {
// Store answers (again).
}
2) Keep IDs in a big array, and store those afterwards.
You can save the answers in a nested array. The key of the main level is the question ID. Per question you store an array (or a string, but I think array is easier) of answers. After inserting the questions and answers, you can loop through the array and insert all the multiple choices.
This should work fine, unless you've got millions of questions. Eventually $allAnswerIDs might become too big for memory, but I don't think it will be a problem soon in this scenario.
$allAnswerIDs = array();
foreach ($entries as $entry) {
if(is_numeric(substr($entry, 0, 1)) === true) {
// Insert question as you do now.
// Store the question ID in the array.
$allAnswerIDs[$questionId] = array();
} else {
// Insert answer.
// Store ID in $answerIDs as an array (or as a string, if you like).
$allAnswerIDs[$questionID][] = $answerID;
}
}
foreach ($allAnswerIDs as $questionID => $questionAnswerIDs) {
// Build a string of the answers per question.
$answerIDs = implode(',', $questionAnswerIDs);
// Insert the multiple choice row using $questionID and $answerIDs.
}
3) Use a bulk insert after the loop.
You can write a single, slightly more complex SQL statement that inserts all of them at once, like:
INSERT INTO multichoice(question, mc_answers)
SELECT
question,
GROUP_CONCAT(choices) as AnswerIDs
FROM
Choice
You can execute this statement once at the end of your script, and it will make the database generate the entire content for the multichoice table at once.
4) Make a view.
As you can see, the (relatively simple) select in method 3 shows all the information you want to have in the multichoice table, so instead of inserting it in the table, you may consider to make it a view instead in the database. That way, you don't have to store the information at all. Your PHP code will be easier, your database cleaner, and you can query from the view as you would from the table.
Related
I am having a bit of trouble with this for loop here and hope to get some help !
So I will recreate a simplified version of the loop here:
foreach($quote as $key => $item) {
if(isset($item['dbid'])){
$q_sql = new mysql_builder2('quote',3);
} else {
$q_sql = new mysql_builder2('quote',2);
}
$q_sql->addArgument('name', $name);
$q_sql->addArgument('curr', $curr);
foreach($term[$key] as $data) {
if(isset($data['term_id']) {
$t_sql = new mysql_builder2('details',3);
} else {
$t_sql = new mysql_builder2('details',2);
}
$t_sql->addArgument('date', $date);
$t_sql->addArgument('term', $termnum);
mysqli_query($dbc, $t_sql->build());
}
mysqli_query($dbc, $q_sql->build());
}
Okay, so I think I got it all right here.
EDIT: Before the loops I have the following :
$quote = $_POST['quotes'];
$term = $_POST['terms'];
Inside the HTML the name of these elements is structured like so:
quotes[1][name]
quotes[1][curr]
terms[1][1][date]
terms[1][1][termnum]
and then if there's a second:
quotes[2][name]
quotes[2][curr]
terms[2][1][date]
terms[2][1][termnum]
terms[2][2][date]
terms[2][2][termnum]
etc..
Explaination:
First, mysqli_builder2 is a premade function that creates SQL queries.. When the value is 3, it sets UPDATE, when it's 2 it does INSERT
Now, what will happen is a user will fill out a form and the data will go into two tables, Quote and Details. For each single entry into Quote, there can potentially be multiple in Details (note: I've left out a lot of fields in my example code to save space but there are links between the 2 tables).
My problem here is when I run this for a very simple UPDATE, the second foreach loops runs one extra time always, and it is always an INSERT with random values for each field.. I can't seem to figure why this is happening because it works 100% properly for the first foreach loop..
Example array output when submit:
Array
(
[0] => UPDATE quote SET job_id = 2, wo_id = 9952, quote_num = '1a', revenue = '100.00', cost = '100.00', currency = 1, term = 1 WHERE id = 5857;
)
Array
(
[0] => UPDATE details SET user_id = 532, job_id = 2, wo_id = 9952, quote_num = '1a', percent = 10, term = 1, active = 1, status = 0, date_picked = '2015-02-04', date_submitted = now() WHERE id = 588;
[1] => INSERT INTO details(user_id, job_id, wo_id, quote_num, percent, term, active, status, date_picked, date_submitted) VALUES(532, 2, 9952, '1a', 6, 6, 1, 0, '1969-12-31', now());
)
This INSERT should not be there at all (notice the date going in) ..
Anyways, I'm kind of stuck here and any help is appreciated. If you need any other info just ask :)
Thanks !
Are you sure you copy the code as is?
you have got syntax error:
if(isset($data['term_id']) {
should be:
if(isset($data['term_id'])) {
so i have a question regarding, hmmm filtering data in mysql/php.
So i have table lets say
ID | fk1 | fk2
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 3 3
Basicaly i have in same table many occurancies of fk1 that are referenced to fk2 and when i output that in table ofcourse i get result as
1 1
1 2
1 3
2 1
etc. And that is normal. What i want to do is basically output one time FK2 value lets say 1, and all corresponding FK1 values below it. Sorta like group by but for each occurancie of FK2 i need all the FK1s that have that FK2 in their row.
I managed to get it working somehow so that all FK1 print out and corresponding FK2 but other way around wont work :S
$FK1 = '';
while($row = mysql_fetch_array($result_set)){
if($FK1 != $row['fk1']){
if($FK1 != ''){
echo $printout;
}
$FK1 = $row['fk1'];
$printout = "<tr class='success'><td>" . "<strong>" . $row['fk1']. "</td></tr>";
}
$printout = $printout .'<td class="warning"> '.$row['fk2']. '</td></tr>';
}
echo $printout ;
Any idea how to do this with some smart loop instead of using multiple queries on base?
Thanks
For each iteration of the loop that prints the query, you can check if the previous iteration printed the same thing and (if yes) skip the printing or print the current one if not.
I hope I understood the question and my answer is clear enough.
Something like this:
$cFk1 = "";
foreach($query_result as $cVal){
if($cVal != cFk1){
print $cVal;
}
$cFk1 = $cVal;
}
You can try GROUP_CONCAT in your query such as:
SELECT
fk2, GROUP_CONCAT(fk1) fk1,
FROM
table
GROUP BY
fk2
And then in your PHP code:
foreach($rows as $row) {
// plain echo
echo $row['fk2'].' '.$row['fk1'];
}
or
foreach($rows as $row) {
// or with post-processing
echo $row['fk2'].' ';
$fk1 = explode(',', $row['fk1']);
foreach($fk1 as $value) {
echo $value.'-';
}
}
But beware - GROUP_CONCAT has its limits, you should make sure the strings don't contain the separator (if it's only numbers, it's ok), etc.
Say I have a query the following query run:
Edit
Added order clause because the real sql statement has one.
SELECT description, amount, id FROM table ORDER BY id
In this instance, the ID is not unique to the dataset. It would return something like this.
Description Amount ID
----------- ------ --
1 Hats 45 1
2 Pants 16 1
3 Shoes 3 1
4 Dogs 5 2
5 Cats 6 2
6 Waffles 99 3
What I need to do is enclose each section of IDs in it's own div (So rows 1,2,3 in one div, 4,5 in another div and 6 in it's own div).
There are tons of solutions to this but I just can't think of one that isn't overly complicated.
I want to be able to keep the SQL how it is and somehow sort the data set in PHP so that I can loop through each section of the dataset while looping through the dataset as a whole.
Some kind of array would work but the structure of it is stumping me.
How can I get this to work? PHP solutions would be idea but theoretical will help too.
See if something like this works for you.
// execute query: Select description, amount, id from table
$results = array();
while ($row = $query_result->fetch_array()) {
if (!isset($results[$row['id']])) $results[$row['id']] = array();
$results[$row['id']][] = $row; // push $row to results for this id
}
// later on
foreach($results as $id => $data) {
// output div based on $id
foreach($data as $datum) {
// output individual data item that belongs to $id
}
}
A simple serial solution might look something like this:
$curId = ''; // track working id
$firstDiv = true; // track if inside first div
// open first div
echo '<div>';
// foreach $row
{
// when id changes, transition to new div, except when in first div
if ($row->$id != $curId) {
if ($firstDiv) {
$firstDiv = false;
} else {
// start new div
echo '</div>';
echo '<div>';
}
$curId = $row->$id; // track new current id
}
// display contents of current row
}
// close last div
echo '</div>';
Just store the id in temp variable, if the next one is different close the div and open new div
Assuming associative arrays for the db results:
$final = array();
foreach($results as $result)
{
$final[$result['id']][] = $result;
}
This leaves you with an associative array $final that groups the entries by ID
ok, a bit of background,
just into codeigniter
not a fan of sql and server-side scripts
i know what joins are
i have a many-to-many database for the first time
it's because joins typically have the following example as a result. but i wanted to parse this without having to build code to ignore repetitions. it's a 3-table join sample. the issue of repeating values increases as i join more tables:
table1.authorid table1.authorname table2.books table3.favorited
1 john john's book 1 jean
1 john john's book 1 joe
1 john john's book 2 ken
1 john john's book 2 mark
2 mark mark's book 1 alice
2 mark mark's book 1 ted
2 mark mark's book 2 sarah
2 mark mark's book 2 denise
is there a way in codeigniter (or plain PHP) that i can get this array form and turn it into something like json (and parse it like json)
$result = [
{
'authorid':1,
'authorname':'john',
'books':['john's book1','john's book2'],
'favorited':['jean','joe','ken','mark']
},
{
'authorid':2,
'authorname':'mark',
'books':['mark's book1','mark's book2'],
'favorited':['alice','ted','sarah','denise']
}
]
Update: this is not limited to this depth of objects/arrays (like in the example). it can go deeper (arrays in arrays, arrays in objects, objects in arrays, objects in objects)
// first, we need the SQL results in the $result_array variable
$sql = 'SELECT ...'; // your SQL command
$result_array = $this->db->query($sql)->result_array(); // codeigniter code
// here the real answer begins
$result = array();
foreach ($result_array as $row)
{
if (!isset($result[$row['authorid']])
{
$author = new StdClass();
$author->authorid = $row['authorid'];
$author->authorname = $row['authorname'];
$author->books = array($row['books']);
$author->favorited = array($row['favorited']);
$result[$row['authorid']] = $author;
}
else
{
if (!in_array($row['books'], $result[$row['authorid']]->books))
{
$result[$row['authorid']]->books[] = $row['books'];
}
if (!in_array($row['favorited'], $result[$row['authorid']]->favorited))
{
$result[$row['authorid']]->favorited[] = $row['favorited'];
}
}
}
$result = array_values($result);
echo json_encode($result);
I'm trying hard to wrap my head around what I'm doing here, but having some difficulty... Any help or direction would be greatly appreciated.
So I have some values in a table I've extracted according to an array (brilliantly named $array) I've predefined. This is how I did it:
foreach ($array as $value) {
$query = "SELECT * FROM b_table WHERE levelname='$value'";
$result = runquery($query);
$num = mysql_numrows($result);
$i=0;
while ($i < 1) {
$evochance=#mysql_result($result,$i,"evochance"); // These values are integers that will add up to 100. So in one example, $evochance would be 60, 15 and 25 if there were 3 values for the 3 $value that were returned.
$i++;
}
Now I can't figure out where to go from here. $evochance represent percentage chances that are linked to each $value.
Say the the favourable 60% one is selected via some function, it will then insert the $value it's linked with into a different table.
I know it won't help, but the most I came up with was:
if (mt_rand(1,100)<$evochance) {
$valid = "True";
}
else {
$valid = "False";
}
echo "$value chance: $evochance ($valid)<br />\n"; // Added just to see the output.
Well this is obviously not what I'm looking for. And I can't really have a dynamic amount of percentages with this method. Plus, this sometimes outputs a False on both and other times a True on both.
So, I'm an amateur learning the ropes and I've had a go at it. Any direction is welcome.
Thanks =)
**Edit 3 (cleaned up):
#cdburgess I'm sorry for my weak explanations; I'm in the process of trying to grasp this too. Hope you can make sense of it.
Example of what's in my array: $array = array('one', 'two', 'three')
Well let's say there are 3 values in $array like above (Though it won't always be 3 every time this script is run). I'm grabbing all records from a table that contain those values in a specific field (called 'levelname'). Since those values are unique to each record, it will only ever pull as many records as there are values. Now each record in that table has a field called evochance. Within that field is a single number between 1 and 100. The 3 records that I queried earlier (Within a foreach ()) will have evochance numbers that sum up to 100. The function I need decides which record I will use based on the 'evochance' number it contains. If it's 99, then I want that to be used 99% of the time this script is run.
HOWEVER... I don't want a simple weighted chance function for a single number. I want it to select which percentage = true out of n percentages (when n = the number of values in the array). This way, the percentage that returns as true will relate to the levelname so that I can select it (Or at least that's what I'm trying to do).
Also, for clarification: The record that's selected will contain unique information in one of its fields (This is one of the unique values from $array that I queried the table with earlier). I then want to UPDATE another table (a_table) with that value.
So you see, the only thing I can't wrap my head around is the percentage chance selection function... It's quite complicated to me, and I might be going about it in a really round-about way, so if there's an alternative way, I'd surely give it a try.
To the answer I've received: I'm giving that a go now and seeing what I can do. Thanks for your input =)**
I think I understand what you are asking. If I understand correctly, the "percentage chance" is how often the record should be selected. In order to determine that, you must actually track when a record is selected by incrementing a value each time the record is used. There is nothing "random" about what you are doing, so a mt_rand() or rand() function is not in order.
Here is what I suggest, if I understood correctly. I will simplify the code for readability:
<?php
$value = 'one'; // this can be turned into an array and looped through
$query1 = "SELECT sum(times_chosen) FROM `b_table` WHERE `levelname` = '$value'";
$count = /* result from $query1 */
$count = $count + 1; // increment the value for this selection
// get the list of items by order of percentage chance highest to lowest
$query2 = "SELECT id, percentage_chance, times_chosen, name FROM `b_table` WHERE `levelname` = '$value' ORDER BY percentage_chance DESC";
$records = /* results from query 2 */
// percentage_chance INT (.01 to 1) 10% to 100%
foreach($records as $row) {
if(ceil($row['percentage_chance'] * $count) > $row['times_chosen']) {
// chose this record to use and increment times_chosen
$selected_record = $row['name'];
$update_query = "UPDATE `b_table` SET times_chosen = times_chosen + 1 WHERE id = $row['id']";
/* run query against DB */
// exit foreach (stop processing records because the selection is made)
break 1;
}
}
// check here to make sure a record was selected, if not, then take record 1 and use it but
// don't forget to increment times_chosen
?>
This should explain itself, but in a nutshell, you are telling the routine to order the database records by the percentage chance highest chance first. Then, if percentage chance is greater than total, skip it and go to the next record.
UPDATE: So, given this set of records:
$records = array(
1 => array(
'id' => 1001, 'percentage_chance' => .67, 'name' => 'Function A', 'times_chosen' => 0,
),
2 => array(
'id' => 1002, 'percentage_chance' => .21, 'name' => 'Function A', 'times_chosen' => 0,
),
3 => array(
'id' => 1003, 'percentage_chance' => .12, 'name' => 'Function A', 'times_chosen' => 0,
)
);
Record 1 will be chosen 67% of the time, record 2 21% of the time, and record 3 12% of the time.
$sum = 0;
$choice = mt_rand(1,100);
foreach ($array as $item) {
$sum += chance($item); // The weight of choosing this item
if ($sum >= $choice) {
// This is the item we have selected
}
}
If I read you right, you want to select one of the items from the array, with some probability of each one being chosen. This method will do that. Make sure the probabilities sum to 100.