Using Joins to Eliminate Multiple SELECTs in Loop - php

I'm trying to teach myself PHP/mysql by building a joe's goals clone, if you will.
Basically each user has multiple goals, and each day they record how many times a certain event occurred. For example, say my goal is to drink only 1 cup of coffee per day. If I had 3 today (oops!), I'd record 3 "checks" for today. I use a table called 'checks' to hold the check count for each day.
I have the following tables, and sample inserts:
CREATE TABLE `users` (
`user_id` int(5) NOT NULL AUTO_INCREMENT,
`user_email` varchar(50) NOT NULL,
`user_name` varchar(25) NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;
-- Dumping data for table `users`
INSERT INTO `users` VALUES (1, 'xxx#xxx.com', 'xxx');
INSERT INTO `users` VALUES (2, 'some#guy.com', 'SomeGuy');
CREATE TABLE `goal_settings` (
`goal_id` int(5) NOT NULL AUTO_INCREMENT,
`user_id` int(5) NOT NULL,
`goal_description` varchar(100) NOT NULL,
PRIMARY KEY (`goal_id`),
KEY `user_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
-- Dumping data for table `goal_settings`
INSERT INTO `goal_settings` VALUES (1, 1, 'Run 1k');
INSERT INTO `goal_settings` VALUES (2, 1, 'Read 20 pages');
INSERT INTO `goal_settings` VALUES (3, 2, 'Cups of Coffee');
CREATE TABLE `checks` (
`check_id` int(40) NOT NULL AUTO_INCREMENT,
`goal_id` int(5) NOT NULL,
`check_date` date NOT NULL,
`check_count` int(3) NOT NULL,
PRIMARY KEY (`check_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
-- Dumping data for table `checks`
INSERT INTO `checks` VALUES (6, 1, '2012-03-02', 3);
INSERT INTO `checks` VALUES (2, 1, '2012-03-01', 2);
INSERT INTO `checks` VALUES (3, 2, '2012-03-01', 1);
INSERT INTO `checks` VALUES (5, 1, '2012-02-29', 1);
The output I'd like has goal_ids as rows and a range of dates as columns (like a week view calendar).
goal_id | 2012-03-01 | 2012-03-02 | 2012-03-03 | ... 2012-03-08 |
--------------------------------------------------------------------
1 2 3 0 ... 0
2 1 0 0 ... 0
Please note that when no checks exist for a given goal on a given day, 0 is returned instead of NULL.
I was able to get it working, poorly, using PHP. Truncated code, but I hope it shows basically what I tried: [$goal_ids is an array holding all goals associated with a user. $num_days is the number of days (i.e. columns) to be displayed, and $goal_days is an array used to hold the days we're looking to get info for].
$mysqli = new mysqli('xxx','xxx','xxx','goals');
$stmt = $mysqli->stmt_init();
$stmt = $mysqli->prepare("SELECT checks.check_count AS check_count
FROM `checks` WHERE goal_id = ? AND check_date = ?");
for($i=0; $i<=$goal_count - 1; $i++){
echo '<tr id="'.$goalid.'">';
for($j=0; $j <=$num_days; $j++){
$checkdate = $goal_days[$j];
$goalid = (integer) $goal_ids[$i];
if (!$stmt->bind_param("ii", $goalid, $checkdate)) {
echo "Binding parameters failed: (" . $stmt->errno . ") " . $stmt->error;
}
if (!$stmt->execute()) {
echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;
}
$stmt->bind_result($check_count);
if($stmt->fetch()){
echo "<td>".$check_count."</td>";
}
else{
echo '<td>0</td>';
}
}
echo "</tr>";
}
echo "</table>";
$stmt->close();
This is obviously inefficient because for m goals and n days, it makes m x n select statements.
From reading, it seems like I'm basically trying to make a pivot table, but I've read that they are inefficient also, and I'm guessing what I'm doing is better handled by PHP than by doing a pivot table?
That leaves me with joins, which is what I think I'm asking for help with. I have considered creating a new column for every day, but I think it's not ideal. I'm open to totally changing the schema if necessary.
I hope I've been clear enough. Any help or pointers in the right direction would be greatly appreciated.
Thanks!

If I understand correctly your problem then I would suggest you should do a regular select in which each combination of goal_id and check_date will get a record in the result set, and then at the client side you will make a column for each check_date say by having an array of arrays and insert the checkcount in it.
This should at least be faster than having m x n select statements.
For more efficiency your sql can sort it by the goal_id and check_date, this will cause the records to be grouped together.
Here is an example of the sql statement:
SELECT check_date, goal_id, check_count FROM checks ORDER BY goal_id, check_date
Here is PHP sample code, assuming you have an array of arrays "$array_of_arrays" (initialized to zero to avoid the null problem) with the outer key being the goal_id and the inner key being the check_date:
while ($row = mysqli_fetch_result($result)){
$row_goal_id = $row["goal_id"];
$row_check_date = $row["check_date"];
$array_of_arrays[$row_goal_id][$row_check_date] = $row["check_count"];
}
And then you can use the array of arrays to do what you like, say for if you wish to output as an HTML table example then join the inner array with </td><td> and the outer array with </td></tr><tr><td>.
An example of how to create and initialize the "$array_of_arrays" array would be as follows (assuming you have an array $goals containing all the goals and an array $dates containing all the dates, if you don't know then in advance you can fetch them from the checks table by doing a SELECT DISTINCT)
$array_of_arrays = array();
foreach ($goals as $key=>$value){
$array_of_arrays[$value] = array();
foreach ($checks as $key1=>$value1){
$array_of_arrays[$value][$value1] = 0;
}
}
A similar approach can be used to generate the final HTML table as follows:
$final_array = array();
foreach ($array_of_arrays as $key=>$value){
$final_array[$key] = implode("</td><td>", $value);
}
$final_str = implode("</td></tr><tr><td>", $final_array);
$table_str = "<table><tr><td>" . $final_str . "</td></tr></table>";

Consider adding a table of days (or creating temporary one at runtime) holding just consecutive dates or dates you need. You could then get a nice list of check counts using a single query:
SELECT g.goal_id, d.day, COALESCE(c.check_count,0) as check_count
FROM
goal_settings g
JOIN
days d
LEFT JOIN
checks c
ON c.goal_id = g.goal_id AND c.check_date = d.day
WHERE
g.user_id = 1
AND d.day BETWEEN '2012-03-01' AND '2012-03-03'
ORDER BY g.goal_id, d.day
resulting in a rowset like:
goal_id | day | check_count
1 | 2012-03-01 | 2
1 | 2012-03-02 | 3
1 | 2012-03-03 | 0
2 | 2012-03-01 | 1
2 | 2012-03-02 | 0
2 | 2012-03-03 | 0
And then fetch those rows in a loop with php to build a nice html table - if goal_id changed then print new row and so on.

Related

Getting the missing IDs from a table by storing the records into an array and compare the set of numbers with a for loop

I'm currently working on a program that gets the missing IDs of a table and the idea that I come up with is that by storing the IDs into an array and use a for loop to check if a number exists in the array and if it's existing then it is classified as a missing ID. I also used the php function - in_array() to check if a number exists in the array.
This is the code that I came up with, but I ended up with just displaying the numbers from the for loop.
<?php
include 'dbconnect.inc'; //just to the the dbconnect for connecting into the database.
$numbers = array(1, 2, 4, 6, 7, 9);
$arrlength = count($numbers);
$query = "SELECT id FROM existing";
$result = mysqli_query($conn, $query);
$existing = array();
while ($row = mysqli_fetch_assoc($result)) {
$existing[] = $row;
}
for ($i=0; $i<7358; $i++) {
if (in_array($i, $existing)) {
echo $i . " is a missing ID <br>";
} elseif(!in_array($i, $existing)) {
echo $i . " exists in the table <br>";
}
}
?>
I prefer this solution than using the temporary tables in an SQL because it takes more than to load the query and it would not be good for a webpage.
Hope that you could help me. Thanks!
From this answer:
To get missing ranges:
SELECT a.id+1 AS 'Missing From', MIN(b.id)-1 AS 'Through'
FROM existing AS a
JOIN existing AS b ON a.id < b.id
GROUP BY a.id
HAVING a.id+1 < MIN(b.id)
fiddle
User variables are only evaluated when sent, so using a HAVING NOT (gap_from=0 AND gap_to=0) clause isn't possible as an optimization (see user variables manual). A such we use the "sending" to be sending to the temporary table to save a larger time full of data that is about to be discarded.
The temporary table uses the primary key ensure there will only be one (0,0) entry that occurs when the there is no gap. Inserting subsequent existing entries (0,0) gets ignored resulting in a minimal table of gaps.
The remainder of the table is the gaps in the sequence:
create table existing (id int unsigned not null)
insert into existing values (3),(5),(6),(7),(8),(19),(20),(21),(30)
set #last=0
CREATE TEMPORARY TABLE v (gap_from int unsigned, gap_to int unsigned, next int unsigned, PRIMARY KEY(gap_from, gap_to))
IGNORE SELECT IF(#last=id, 0, #last) as gap_from,
IF(#last=id, 0, id-1) as gap_to,
#last:=id+1 as next
FROM existing ORDER BY id
select gap_from,gap_to from v where NOT (gap_from=0 AND gap_to=0)
gap_from | gap_to
-------: | -----:
0 | 2
4 | 4
9 | 18
22 | 29
If you don't want the first gap, the one between 0 and the first entry in the table:
select gap_from,gap_to from v where gap_from!=0
db<>fiddle here

Only Insert if unique(check 2 rows)

I am learning SQL and I am in situation where I have to INSERT values into database only if it do not exist already.
My tableview structure is like this:
+----------+-----------+-----+-----+
| first_id | second_id | timestamp |
+----------+-----------+-----------+
I Want to insert only if there is not same first_id and second_id e.g if in table there is first_id 1 and second_id 2 and I am adding it again, I do not want to add it anymore. So if the first_id and second_id rows already have values 1 and 2 then do not add but if first_id is 3 and second_id is 1 then I would allow the inserting.
This is my query ATM:
INSERT INTO `testtable`.`ids` (`first_id`, `second_id`) VALUES (:first_id, :second_id)
And like this I tried with NOT EXISTS but it is not working:
NOT EXISTS (SELECT first_id, second_id FROM `testtable`.`ids` WHERE first_id = : first_id AND second_id = : second_id) INSERT INTO `testtable`.`ids` (`first_id `, `second_id `) VALUES (: first_id, : second_id)
The last mentioned query gives me Syntax error but once I even got the integrity violation and it told me to check documentation.
I am executing my queries using PHP ->query(""); function.
I tried to do like IF NOT EXISTS and NOT EXISTS but those didn't work. How should I approach to this?
This is simple. Declare first_id and second_id as composite key. I would prefer not to make any changes in your PHP Code but make your DB structure versatile so that it doesn't accept any duplicate values how-so-ever you are inserting it.
CREATE TABLE `demo` (
`first_id` smallint(6) DEFAULT NULL,
`second_id` smallint(6) DEFAULT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `first_id` (`first_id`,`second_id`)
)
Now first_id and second_id will never accept duplicate values.
^ table contains values (1,2). Now inserting (1,3).
^the table accepts (1,3). Now inserting (1,2) again.
the insert statement throws an error. Now the table will never accept duplicate values for the key (first_id,second_id).
If the table already exists and you're not creating it from scratch, simply execute:
alter table `table_name` add unique key (first_id, second_id);
This will prevent the duplicate values henceforth.
You may try this if you are using PHP and MySql:
<?php
//Added database connection code here
$first_id = $_POST['first_id '];
$second_id = $_POST['second_id '];
$sql = "select * from ids where first_id = ".$first_id ." and second_id ='".$second_id."'" ;
$result = $mysqli->query($sql);
$row = $result->fetch_row();
if($row[0]) {
$mysqli->close();
} else {
//preapare an insert statement
$stmt = $mysqli->prepare(" INSERT INTO `ids` (first_id, second_id) VALUES (?,?)");
$stmt->bind_param("ii", $first_id, $second_id);
//execute the statement
if( $stmt->execute() ) {
unset($first_id);
unset($second_id);
} else {
echo $mysqli->error;
}
//close statement
$stmt->close();
$mysqli->close();
}
?>
Checking for existence for the first and second ids and additionally declare 2 sql variables for first and second ids and set them as per required.
DECLARE #Exists int; #first_id int; #second_id int;
SET #first_id = 1;
SET #second_id = 2;
SELECT #Exists = COUNT(*) FROM [testtable] where [first_id] = #first_id and [second_id] = #second_id;
Condition to insert if count of matched records is 0:
IF(#Exists = 0)
BEGIN
INSERT INTO [testtable](first_id, second_id)
VALUES(#first_id,#second_id)
END

PHP mySQL Time Between Rows

Hello I am stuck on this. Looking to be able to pull the time between 2 start/stop rows from a mySQL database. My table looks like this
fillercdown_ndx | time | B3_4_5
1 | 2016-06-16 14:59:45 | 0
2 | 2016-06-16 15:03:11 | 1
Basically when its recorded as 0 the machine stopped and when the record is 1 the machine restarted. I need to be able to calculate the amount of time the machine was down between certain times like 8AM-5PM. Was going to use PHP to display it upon users time entered, but have no idea on the SQL command.
Anyone know the best way to be able to find this?
Create table
CREATE TABLE `fillercdown` (
`fillercdown_ndx` int(11) NOT NULL AUTO_INCREMENT,
`time` datetime DEFAULT NULL,
`B3_4_5` int(11) DEFAULT NULL,
PRIMARY KEY (`fillercdown_ndx`),
KEY `fillercdowntimendx` (`time`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1
UPDATE:
There will be hundreds of these "start" and "stop" entries. My end all goal would be in php to give the user a small form asking them to provide a time range like 8AM-5PM and then be able to calculate all the time where the machine was "stopped" (which is when B3_4_5 is at 0). I just can't seem to figure out the right SQL call to get the time differences between each 0 and 1 and add them together between a set time range
This is what I am currently using to do the same thing it sounds like you're trying to do. I'm using PDO but this could be adapted fairly easily for mysqli if you need to use that. This depends on alternating off/on values by time. If there are any consecutive off or on rows for whatever reason, the expected result becomes ambiguous.
// First select everything in the requested range ordered by time.
$sql = "SELECT `time`, B3_4_5 FROM your_table WHERE `time` BETWEEN ? AND ? ORDER BY `time`";
$stmt = $pdo->prepare($sql);
$stmt->execute([$query_start, $query_end]);
// Initialize two DateTime objects (start and end) used to calculate the total time.
$total_start = new DateTime('00:00');
$total_end = clone $total_start;
$off = null;
while ($row = $stmt->fetchObject()) {
if ($row->B3_4_5) {
$on = new DateTime($row->time);
// increment total end time with difference between current off/on times
if ($off) {
$total_end->add($on->diff($off));
}
} else {
$off = new DateTime($row->time);
}
}
// total downtime is difference between start and end DateTimes
$total_downtime = $total_start->diff($total_end);
$total_downtime is a DateInterval object. You can get return a message using its format method:
echo $total_downtime->format('Total downtime: %h hours, %i minutes, %s seconds.');
This is the basic idea... It selects the result into a single row andcolumn, which you can then fetch withPHP`...
This solution assumes that stops & starts come in pairs, i.e: the ID of a start will be +1 that of a stop. Otherwise you should SELECT/JOIN the ID that is > that of the stopped one, and LIMIT it to 1.
A self join might not yield optimal performance wise, so be careful and measure execution times with some data to be on the safe side.
http://sqlfiddle.com/#!9/de72bf/1
CREATE TABLE fillercdown (
`fillercdown_ndx` int(11) NOT NULL AUTO_INCREMENT,
`time` datetime DEFAULT NULL,
`B3_4_5` int(11) DEFAULT NULL,
PRIMARY KEY (`fillercdown_ndx`),
KEY `fillercdowntimendx` (`time`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
INSERT INTO fillercdown
(`time`, `B3_4_5`)
VALUES
('2016-06-16 14:00:45', 0),
('2016-06-16 14:01:00', 1),
('2016-06-16 16:00:00', 0),
('2016-06-16 16:01:00', 1)
;
SELECT SUM(TIMEDIFF(g.`time`, f.`time`))
FROM fillercdown f
INNER JOIN fillercdown g
ON g.`fillercdown_ndx` = (f.`fillercdown_ndx` + 1)
AND g.`B3_4_5` = 1
AND TIME(g.`time`) BETWEEN '08:00:00' AND '17:00:00'
WHERE f.`B3_4_5` = 0
AND TIME(f.`time`) BETWEEN '08:00:00' AND '17:00:00'
If you want to include times where the machine stopped but has not yet restarted, you can do something like this:
http://sqlfiddle.com/#!9/d113b9/9
INSERT INTO fillercdown
(`time`, `B3_4_5`)
VALUES
('2016-06-16 14:00:45', 0),
('2016-06-16 14:01:00', 1),
('2016-06-16 16:00:00', 0),
('2016-06-16 16:01:00', 1),
('2016-06-16 16:02:00', 0)
;
SELECT SUM(TIMEDIFF(COALESCE(g.`time`, '2016-06-16 16:02:01'), f.`time`))
FROM fillercdown f
LEFT JOIN fillercdown g
ON g.`fillercdown_ndx` = (f.`fillercdown_ndx` + 1)
AND g.`B3_4_5` = 1
AND TIME(g.`time`) BETWEEN '08:00:00' AND '17:00:00'
WHERE TIME(f.`time`) BETWEEN '08:00:00' AND '17:00:00' AND f.`B3_4_5` = 0
You could replace 2016-06-16 16:02:01 with NOW() or something based on f.time, it depends on your application needs of course.
If you never want to get NULL but rather 0, if there are no matching rows, then do something like: COALESCE(SUM(...), 0).
If you prefer a scripted solution you can always do something like this:
SELECT f.`time`, f.`B3_4_5`
FROM fillercdown f
WHERE TIME(f.`time`) BETWEEN '08:00:00' AND '17:00:00'
And then compute the sum, like so (psuedocode):
stopped = null;
pairs = []
for ( row in rows )
if ( row.isStopped ) stopped = row
else
pairs += [stopped, row]
stopped = null
sum = 0
for ( pair in pairs )
sum += duration( pair[0], pair[1] )

Inserting a multidimensional array into a MySQL Table text field

I'd like to insert a multidimensional array into a MySQL Database field so that it can then easily be read from the database at a later date back into an array. What's the best way to achieve this?
I've tried the following to no avail:
$id = "MXB-487"
$items = Array(Array("Coffee", "Blend", "500"), Array("Coffee1", "Blend1", "250"));
$items = implode(",", $items);
mysqli_query($con,"INSERT INTO carts (id, items)
VALUES ($id, $items)");
/*Code that pulls the array from the Database based on id and stores in variable $info*/
restored_mdarray = explode(",", $info);
ID in MySql, is usually unique (I'm pretty sure you specified it that way). So, you can't share the ID for multiple items. Also, imploding will end up with the following query:
INSERT INTO carts (id, items) VALUES(MXB-487, Array, Array)
Because you have a multidimensional array you're trying to implode, it doesn't recursively implode.
What you should do is loop through the objects, and I'm not sure how the relationship here works, but it looks like you need a relation table to connect those items. Consider the following structure:
Carts:
+----------+-----------+
| ID | Name |
+----------+-----------+
--<-| MXB-487 | Blah blah |
| +----------+-----------+
|
| Items:
| +----------+-----------+----------+-----------+
| | Cart_ID | Type1 | Type 2 | Amount |
| +----------+-----------+----------+-----------+
--->| MXB-487 | Coffee | Blend | 500 |
+----------+-----------+----------+-----------+
And in order to implement that in PHP, you'd do something like this:
<?php
$id = "MXB-487";
$items = array(
array("Coffee", "Blend", "500"),
array("Coffee1", "Blend1", "500"),
);
$sql = "INSERT INTO actions (cart_id, type1, type2, amount) VALUES ";
$items_sql = array();
if (count($items)) {
foreach ($items as $item) {
$items_sql[] = "('$id', '{$item[0]}', '{$item[1]}', '{$item[2]}')";
}
}
$sql .= implode(", ", $items_sql);
And then run the query.
It will look like this:
INSERT INTO actions (cart_id, type1, type2, amount) VALUES ('MXB-487', 'Coffee', 'Blend', '500'), ('MXB-487', 'Coffee1', 'Blend1', '500')
Which you can later select as such:
<?php
$id = "MXB-487";
$sql = "SELECT * FROM actions WHERE (cart_id = '$id')";
Though as a side note, I suggest you look at PDO and how to bind values, or at least learn to escape your values in the SQL to prevent future injections.
I speculated the structure of the tables, of course you can modify to your needs.
To connect the tables properly via SQL (to fasten the fetching later on) you can use FOREIGN KEY when you define the table:
CREATE TABLE actions (
id INT(11) NOT NULL AUTO_INCREMENT,
cart_id VARCHAR(32) NOT NULL,
type1 VARCHAR(32) NOT NULL,
type2 VARCHAR(32) NOT NULL,
amount INT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (cart_id) REFERENCES carts(id)
)
Use serialize:
$a = array(array(1,2,3), array(3,4,5));
$b = serialize($a);
# write $b to and later read $b from database
$c = unserialize($b);
$a == $c # => true

Too many MySQL queries?

I have a question if anyone can answer. Please excuse my inexperience with this, but this is my first project that I have attempted and all of this is really new to me. I am in the process of trying to build an inventory system at work using php and mySQL and I have hit a bit of a wall regarding how I am going to display the items that are currently loaned out to people.
I have the items that are being provisioned to users broken down into 4 categories and records of the loans for these items are stored into 4 different tables. I also have another table for users, as well as tables for the items, and their characteristics.
What I want when my page is displayed to to have all of the items that are assigned to each user grouped together in a table. I have two ideas on how I can do this, but I'm not sure which would be the best way.
My first thought was to pull all of the users from the users table and store the information into an array, then pull all of the information from the 4 loan tables and store each table into an array. From there I would do something like
for($i=1;$i>sizeof($usersArray);$i++){
for($a=1;$a>sizeof($loanTable1Array);$a++){
if($userArray[$i][userID] == $loanTable1Array[$a][userID]{
//list items
}
}
for($b=1;$b>sizeof($loanTable2Array);$b++){
if($userArray[$i][userID] == $loanTable2Array[$b][userID]{
//list items
}
}
for($c=1;$c>sizeof($loanTable3Array);$c++){
if($userArray[$i][userID] == $loanTable3Array[$c][userID]{
//list items
}
}
for($d=1;$d>sizeof($loanTable4Array);$d++){
if($userArray[$i][userID] == $loanTable4Array[$d][userID]{
//list items
}
}
}
My concern with this though is that I will have around 100-150 users and each table will have an average of 100 different items. This would mean around 40,000 - 60,000 iterations of the loop.
My other idea was to do pull all of the entries from the user table, then use that data to query the other 4 tables using the userID in a where statement like this. But then I read that if you have a query in a loop then you're doing it wrong.
$sql = "SELECT userID FROM users";
$allUsers = runQuery($sql); //data is sanitized before running the query
for($i = 1; $i<sizeof($allUsers); $i++){
$loan1sql = "SELECT * FROM loantable1 WHERE userID = {$allUsers[$i][$userID]}'";
$loan1Items= runQuery($loan1sql);
for($a = 1; $a<sizeof($loan1Items); $a++){
//list items
}
$loan2sql = "SELECT * FROM loantable2 WHERE userID = '{$allUsers[$i][$userID]}'";
$loan2Items= runQuery($loan2sql);
for($b = 1; $b<sizeof($loan2Items); $b++){
//list items
}
$loan3sql = "SELECT * FROM loantable3 WHERE userID = '{$allUsers[$i][$userID]}'";
$loan3Items= runQuery($loan3sql);
for($c = 1; $c<sizeof($loan3Items); $c++){
//list items
}
$loan4sql = "SELECT * FROM loantable4 WHERE userID = '{$allUsers[$i][$userID]}'";
$loan4Items= runQuery($loan4sql);
for($d = 1; $d<sizeof($loan1Items); $d++){
//list items
}
}
Doing this would result in 400 - 600 calls to the database each time the page is loaded. Does anyone have any input on what my best course of action would be? Any help would be greatly appreciated.
By considering an extra category column , you could have one loantable instead of four . Then you would just use one query by JOINing the tables .
Just an example showing one way to do it :
-- Table structure for table `users`
CREATE TABLE IF NOT EXISTS `users` (
`userID` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`userID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
-- Dumping data for table `users`
INSERT INTO `users` (`userID`) VALUES
(1),
(2);
-- --------------------------------------------------------
-- Table structure for table `loantable`
CREATE TABLE IF NOT EXISTS `loantable` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`category` int(11) NOT NULL,
`userID` int(11) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
-- Dumping data for table `loantable`
INSERT INTO `loantable` (`ID`, `category`, `userID`) VALUES
(1, 1, 1),
(2, 2, 1),
(3, 3, 1),
(4, 1, 2),
(5, 3, 2);
Then you would use just one query like :
SELECT *
FROM
`users`
LEFT OUTER JOIN loantable ON loantable.userID = users.userID
WHERE 1
ORDER BY
users.userID
,category
(refer to answers above. This was too long to add as a comment, but I thought it would be helpful)
#cartalot and #Uours THANK YOU!!!! - I had considered creating one table for all of the loans early on but didn't know how to implement it. This makes perfect sense though. My whole issue was confusing the foreign key - parent key constraints in mySQL with how you can actually join tables to display information on your page.
Not to sound like a complete moron, but I think this might be constructive to someone that reads this down the road. I got confused by how you can create fk - pk relations in myPHPAdmin and what they actually do. I though that these relations were necessary to join tables (obviously wrong). I saw the visual connections and though that these tables were somehow "connected".
I know understand that when you create a foreign key parent key restraints all you are basically doing is limiting they data that you can enter into a table based on what is in another table. You can still join information from different tables without these constraints.

Categories