I have table entries in my database with structure:
id | card | event | time | day | hm
> id - is unique ID of each entry.
> card - is number.
> event - is number (0, 1, 2, or 3)
> time - is mysql_timestamp
> day - is day of month when entry was added (date("j", time())
> hm - some other info...
I want to display all entries for one month in html table.
Now I'm using this simple query:
$sql = "SELECT event, time, day, hm FROM `entries`
WHERE card=$card_t
AND YEAR(time)=$year
AND MONTH(time)=$month
AND event IN (0,1,3)
ORDER BY `time` ASC;";
Conditions of output:
table must have minimum one row for a day (31 rows for January).
if there is no entries for day(1-31) row must be:
if there is only two entries for day row must be:
<tr>
<td>$day_number</td>
<td>$time_of_first_event</td>
<td>$first_event</td>
<td>$time_of_last_event</td>
<td>$last_event</td>
</tr>
if there is more than two entries a day two must be (I will hide second one with jquery later):
<tr>
<td>$day_number</td>
<td>$time_of_first_event</td>
<td>$first_event</td>
<td>$time_of_last_event</td>
<td>$last_event</td>
</tr>
<tr>
<table>
<tr>
<td>$time</td>
<td>$event</td>
</tr>
</table>
</tr>
So it looks like:
| 1 | no entry |
| 2 | no entry |
| 3 | no entry |
| 4 | time_first | event_fisrt | time_last |event_last |
| time2 | event 2 |
| time3 | event 3 |
| 5 | no entry |
...
But all i got now is output of 31 row and if one day have more than it duplicates this day row and displays all rows for this day:
| 1 | no entry |
| 2 | no entry |
| 3 | no entry |
| 4 | time1 event 1 |
| 4 | time2 event 2 |
| 4 | time3 event 3 |
| 5 | no entry |
...
Please help me guys and sorry for mistakes, english is not my native lang.
Use GROUP_CONCAT in sql and explode foreach loop
$sql =
"SELECT
GROUP_CONCAT(event SEPARATOR ","), GROUP_CONCAT(time SEPARATOR ","),
day , GROUP_CONCAT(hm SEPARATOR ",") FROM `entries`
WHERE
card=$card_t
AND YEAR(time)=$year
AND MONTH(time)=$month
AND event IN (0,1,3)
GROUP BY
day
ORDER BY
`time` ASC;";
In foreach,
foreach ($entries as $entry) {
if ($entry['event'] == "") {
echo '<tr>';
echo '<td>'.$no++.'</td>';
echo '<td>No events</td>';
...................
echo </tr>';
continue;
}
$events = explode("," , $entry['event']);
if (count($events) > 0) {
// Have multiple row here
foreach($events as $event) {
........................
}
} else {
echo '<tr>';
echo '<td>'.$no++.'</td>';
echo '<td>'.$entry['event'].'</td>';
...................
echo </tr>';
}
}
You want to group the output, if I understand you correctly. And the criterion for building the groups is the date. One way would be to define a variable before your loop, in which you store the date of the last processed entry. And before you output the entry, you check whether the entry's date is the same as in this variable...then you are still within a group and should output it differently.
Might be a little difficult to achieve this in your case because of your very special output format. It might be easier to first loop over the query result and store the entries in a multidimensional array where the date is the key of the first level and in the next level you store all the entries related to this date. So you get:
array (
'xxxx-xx-xx' => array(entry1, entry2, entry3),
'yyyy-yy-yy' => array(entry1),
...
)
Then you can do another loop over this array and use if (count($array[$currentKey]) == 1) to check whether there is only a single event for this date or more.
Related
Okay, so I have a table (timetable) in a database (calendar) which if formatted like so:
+----+--------+-----------+-----------+---------+
| id | name | day | startTime | endTime |
+----+--------+-----------+-----------+---------+
| 1 | George | Sunday | 12:00 | 14:00 |
| 2 | Dan | Monday | 13:30 | 15:30 |
| 3 | Jeff | Wednesday | 12:00 | 14:00 |
| 4 | Bill | Monday | 13:45 | 15:45 |
+----+--------+-----------+-----------+---------+
Then I have some PHP as follows:
<?php
$sql = "SELECT * FROM timetable WHERE id IN (1, 2, 3, 4)"
$result = $con->query($sql);
while ($row = mysqli_fetch_assoc($result)) {
$array2[] = $row;
}
echo json_encode($array2, JSON_PRETTY_PRINT);
?>
And that outputs the entire table, as an associated array in JSON format.
I want it to only output the two that clash. There will never be more than two that clash in the database due to the input method, so it only needs to check if there's one clash, and output the JSON for both rows.
I'm not sure where to start. Is it better to do it programatically via PHP, or is there a way to do with with mysql? I was thinking if it's done programatically, maybe two nested for loops and if statements for greater than or less than on the time fields? But seems messy, and I'm thinking there's likely a more clever end elegant solution that I can't think of.
Thank you for any help you can provide.
You can do it with JOIN in SQL like below:-
To find "any overlap", you compare the opposite ends of the timeframe with each other.
SELECT * FROM timetable a
JOIN timetable b
on a.starttime <= b.endtime
and a.endtime >= b.starttime
and a.name != b.name;
I have a horizontal table which displays items based on date range. The date range is obtained from user input which is start date and end date. I place the start date and end date into an array, and I want to retrieve Item from database based on that date range. Lets say one date have more than one item, so I want all items in that particular date to be placed together in one cell only.
The table structure that I want should look like this:
-----------------------------------------------
| DATE | 2015-12-02 | 2015-12-04 | 2015-12-13 |
|------|------------|------------|------------|
| ITEM | A | B | C, D |
-----------------------------------------------
However, what I get for now is a separated cell of C and D. The structure looks like this:
-----------------------------------------------
| DATE | 2015-12-02 | 2015-12-04 | 2015-12-13 |
|------|------------|------------|------------|-----------
| ITEM | A | B | C | D |
----------------------------------------------------------
So, my question is, how to get the data combined in one cell based on date range array.
Here is some parts of my code:
(excluding the code for getting the date range because I want to focus on the items to be displayed)
$daterange = array();
$daterange = createDateRangeArray(convertDate($from),convertDate($to));
foreach ($daterange as $value) {
$result=mysql_query("SELECT * FROM tblitem WHERE date >= '".$value." 00:00:00' AND date <= '".$value." 59:59'");
while($row=mysql_fetch_array($result)){
$item = $row['item'];
echo "<td>".$item."</td>";
}
}
I have this data from data base.
+----+------------+----------+
| id | date time | duration |
+----+------------+----------+-----------
| 3 | 2012-12-20 09:28:53 | ? |
| 1 | 2012-12-20 19:44:10 | ? |
| 2 | 2012-12-23 16:25:15 | |
| 4 | 2012-12-23 18:26:16 | |
| 4 | 2012-12-24 08:01:27 | |
| 5 | 2012-12-29 20:57:33 | |
| 5 | 2012-12-29 20:57:33 | |
+----+------------+----------+------------
duration for id #1 should be equal to the date id #2 - id #1
duration for id #2 should be equal to the date id #3 - id #2
While if the id is the same, it will be added.
Sorry this is only in my mind, still very new in php so I don't know how to start.
Any help is appreciated.
Thanks
edited sorted by date. duration or total time for 1st record = 2nd record - 1st record
If I understand you correctly there is something which is called id=3 and it starts at "2012-12-20 09:28:53" then it ends at "2012-12-20 19:44:10" and in the same second something id=3 starts and you want to know how long everything lasts?
I would do a loop for all records, but I'd start from ending (in SQL: ...ORDER BY date_time DESC), assuming that end of the last (ie. id=5 which started on 2012-12-29 20:57:33) is now, then I calculate duration as substraction of dates (beginning and end), then I would take event beginning as end of the previous one and so on.
An example (not tested):
$end=now();
$dbh=new PDO(...); // here you need to connect to your db, see manual
while($record=$dbh->query('SELECT id, datetime FROM table_name ORDER BY datetime DESC')->fetchObject()){
$start=$record->datetime;
echo $duration=$end-$start; // convert $end and $start to timestamps, if necessary
$end=$start; // here I say that next (in sense of loop, in fact it is previous) record will end at the moment where this record started
}
This doesn't sum because I don't know how are you going to store your data, but I think you will manage with this.
EDITED
At the beginning I define an array:
$durations=array(); // this will hold durations
$ids=array(); // this will hold `id`-s
$last_id=-1; // the value that is not existent
Then the code follows and instead of echo I put this:
$duration=$end-$start;
if($last->id==$record->id){ // is this the same record as before?
$durations[count($durations)-1]->duration+=$duration; // if yes, add to previous value
}
else { // a new id
$durations[]=$duration; // add new duration to array of durations
$ids[]=$record->id; // add new id to array of ids
$last_id=$record->id; // update $last_id
}
and then $end=$start as above.
To view all durations and ids simply
for($i=0;$i<count($durations);$i++){
echo 'id='.$ids[$i].', duration='.$durations[$i].'<br />';
}
Note these tables are in reverse order.
I have a log` that saves log records (amount earned, etc) of employees and a code that separates the data into tables grouped under each employee id:
Empid: 0001
---------------------------
| Logid | Hours | Pay |
---------------------------
| 1001 | 10 | 50 |
---------------------------
| 1002 | 2 | 10 |
---------------------------
Empid: 0003
---------------------------
| Logid | Hours | Pay |
---------------------------
| 1003 | 3 | 9 |
---------------------------
| 1004 | 6 | 18 |
---------------------------
I managed this with the following semi-pseudocode:
$query = mysql_query("SELECT * FROM `log` ORDER BY empid");
$id = 0;
while ($list = mysql_fetch_assoc($query)) {
if ($id != $list['logid']) {
create header (Logid, Hours, Pay)
$id = $list['logid'];
}
add each data row for the empid
}
But now I would like to add the total of the Pay column and put it at the bottom of each table for each empid.
By putting the code $total_pay = $total_pay + $list['pay'] in the while loop I can get the total pay but I can't figure out how I might be able to show the total at the bottom.
Would really appreciate any advice on this!
This should do it. You basically sum up until the id is changing.
$sum = 0;
while ($list = mysql_fetch_assoc($query)) {
if ($id != $list['logid']) {
//create the totals using $sum !!!
// after that re-set sum to 0
$sum = 0;
//create header (Logid, Hours, Pay)
$id = $list['logid'];
}
$sum += $list['Pay'];
//add each data row for the empid
}
Also...
Please, don't use mysql_* functions in new code. They are no longer maintained and are officially deprecated. See the red box? Learn about prepared statements instead, and use PDO, or MySQLi - this article will help you decide which. If you choose PDO, here is a good tutorial.
There are two ways that you can do this.
PHP
Keep a running total of all of the "pay" values, and add it into your table at the bottom. For example:
$i=0;
while ($list = mysql_fetch_assoc($query)) { // for each row in your results
if ($id != $list['EmployeeId']) { // We only enter this loop if the EmployeeId doesn't equal $id. This can happen because either $id doesn't exist yet, or it doesn't match the previous EmployeeId
$i++; // increase $i by 1
if($i>1) { // Enter this loop only if $i is greater than or equal to 2 (if it is less than two, then this is our first time running this script, and adding a footer row wouldn't make any sense).
create footer (EmployeeId, Hours, Pay); // Log Id is irrelevant here
}
// reset your variables here
$id = $list['EmployeeId']; // set $id = the first or the new Employee ID
$total_pay = $list['pay']; // This is our first time for this Employee, so don't just add it to the running total
create header (EmployeeId, Hours, Pay) // Create the top half of your table
} else { // The EmployeeId has been established: we only need to change the running total
$total_pay = $total_pay + $list['pay'];
}
// add a data row for each LogId. This executes every time we go through the loop
create_normal_row(LogId, EmployeeId, Hours, Pay)
}
// At this point, both Employees have a header, and all data rows. However, we left the loop before we could add the last Employee's footer row
// Let's add one more footer row for the last user
create_footer (Logid, Hours, Pay);
SQL
MySQL has a function that does something very similar to what you are trying to do called ROLLUP. You can read more about it here:
http://dev.mysql.com/doc/refman/5.0/en/group-by-modifiers.html
Basically, you would change your query to work like this:
SELECT LogId, EmployeeId, SUM(Hours), SUM(Pay) FROM `log`
GROUP BY empid, logid WITH ROLLUP
This query will return a dataset that looks like this:
---------------------------------------
| Logid | EmployeeId| Hours | Pay |
---------------------------------------
| 1001 | 1 | 10 | 50 |
---------------------------------------
| 1002 | 1 | 2 | 10 |
---------------------------------------
| NULL | 1 | 12 | 60 |
---------------------------------------
| 1003 | 2 | 3 | 9 |
---------------------------------------
| 1004 | 2 | 6 | 18 |
---------------------------------------
| NULL | 2 | 9 | 27 |
---------------------------------------
| NULL | NULL | 21 | 87 |
---------------------------------------
Whenever $list['Logid'] is null, you know that you have a "total" row. Be careful though, this will add a "sum of all employees" row at the bottom of your dataset. If $list['EmployeeId'] is null, then you know you're in this "total" row.
On a related note (I'm not sure if this is what you're asking for), you can show this stuff in a table by using HTML <table> elements.
Each row would look like this:
<table> <!-- shown at the beginning of each table -->
<tr> <!-- shown at the beginning of each row -->
<td> <!-- shown at the beginning of each table cell -->
Your text goes here
</td> <!-- shown at the end of each table cell -->
<td>
More text can go here
</td>
</tr> <!-- shown at the end of each row -->
</table> <!-- shown at the end of each table -->
<tr>s can be repeated indefinitely within each <table>, and <td>s can be repeated within <tr>s.
I have a table that look like this:
Name | date1 | date2 | date3 | etc..
per1 | status1 | | status2 | etc
per4 | status2 | status3 | | etc
The number of the dates columns is not fixed. Their values can either be a status or they can be empty.
I want to access the data of the dates columns for each row separately and process the data.
The output I want to achieve:
Name | field1 | status1 | status2 | etc..
per1 | value | #ofstat1 | #ofstat2 | etc
So for I got, accessing the table at the beginning of the question:
$confirmed ="Confirmed";
$accepted ="Accepted";
while ($row = mysql_fetch_array($result)) {
$confirmed_cnt =0;
$accepted_cnt =0;
foreach ($row as $value) {
if (strcmp($value, $confirmed)) $confirmed_cnt++;
else if (strcmp($value, $accepted)) $accepted_cnt++;
}
print("<tr>");
print("<td>$row["Name"]</td>"); // name
print("<td>$confirmed</td>"); // confirmed
print("<td>$accepted</td>"); // accepted
print("</tr>");
}
As far as I know this should work, but for some reason it goes trough each column 2 times in a row.
Try mysql_fetch_assoc() or mysql_fetch_row() instead.
mysql_fetch_array() returns every column two times: as [1] and as ['fieldName']
Also, you have another error, replace your following line:
print("<td>$row["Name"]</td>"); // name
for this one:
print("<td>$row['Name']</td>"); // name