MySQL - select missing dates from table? - mysql

Suppose you have the following table values:
date | value
2012-01-01 | 8
2012-01-02 | 3
2012-01-03 | 17
2012-01-09 | 100
2012-01-12 | 2
Now suppose you want to select all the dates between 2012-01-02 and 2012-01-12 and show their values if present. If you simply query the table for the appropriate date range, the dates that don't have values are going to be absent, for obvious reasons. Is there a way to fill in those dates in the query?
The obvious solution is to create a table dates that just stores a list of all dates that may come up, and then to select from the dates table and join values to it, but I'd like to have a solution that doesn't rely on creating a single-column table if I can.
Of note: there are existing questions on SO on this topic, but they are all from 2010 (at least the ones I found when searching were), and MySQL features have grown in that time; there may be a dynamic solution now. If that's not the case, and the dates table is still the best solution, then this question should be closed as a duplicate.

The lack of answers from others suggests to me that at the current time, it is not possible to traverse a range of dates in MySQL without a table that holds those dates. I have, however, written some code in PHP that I'm using to fill in the missing dates after the fact:
function formatResults($inbound, $from, $to) {
$results = array();
$count = 0;
// In order not to lose any results, we have to change how the results are referenced
$indexes = array();
$stats = array();
foreach ($inbound as $stat) {
// ['listindex'] is the date, renamed in the query
$stats[$stat['listindex']] = $stat;
}
// In a function in case you want to pop it out
function dateArray($from, $to) {
$begin = new DateTime($from);
$end = new DateTime($to);
$interval = DateInterval::createFromDateString('1 day');
$days = new DatePeriod($begin, $interval, $end);
$baseArray = array();
foreach ($days as $day) {
$dateKey = $day->format("Y-m-d");
$baseArray[] = $dateKey;
}
$baseArray[] = $to;
return $baseArray;
}
$indexes = dateArray($from, $to);
// Now all the rows we need to return are uniquely identified in $indexes
// So we traverse $indexes to create our results array, rather than relying on $inbound
foreach($indexes as $index) if ($index != '') {
$data = array();
// Make sure we do not run into any 'missing index' problems
if (!isset($stats[$index]))
$stats[$index] = array(
'listindex' => $index,
// ... populate full list of empty fields
);
foreach ($stats[$index] as $key => $value) {
$data[] = $value;
}
$results[$count] = $data;
$count++;
}
return $results;
}

Related

How to get last row in each group in code igniter? [duplicate]

I am working in codeigniter, i have a below table of schemes, my problem is i want to show last updated data of each scheme. I dont know how to do that, please help.
my_table
scheme_code updated_on scheme_name
1 2015-04-13 One
3 2015-04-12 Three
4 2015-04-13 Four
3 2015-04-10 Three
3 2015-04-8 Three
1 2015-04-10 One
4 2015-04-11 Four
My Model
function show_last_updated_data(){
$this->db->select('*');
$this->db->from('my_table');
$this->db->order_by('updated_on', 'DESC');
$query = $this->db->get();
return $query->result();
}
Output Needed
scheme_code updated_on scheme_name
1 2015-04-13 One
3 2015-04-12 Three
4 2015-04-13 Four
My answer is based on #Syed Qarib. I modified it to be compatible with codeigniter active record format.
function show_last_updated_data() {
$this->db->select('*, str_to_date(updated_on, "%Y-%M-%d") As date', false); // false to skip escape
$this->db->from('scheme');
$this->db->group_by('scheme_code');
$this->db->order_by('date', 'DESC');
$query = $this->db->get();
return $query->result();
}
Edit
In another way,
function show_last_updated_data() {
$max = $this->db->select_max("updated_on")->group_by("scheme_code")->get('scheme')->result_array();
$updated_on = array();
if (count($max)) {
$updated_on = array_column($max, "updated_on"); // make sure your php version is >= 5.5
}
$this->db->select("*");
$this->db->from("scheme");
$this->db->where_in("updated_on", $updated_on);
$query = $this->db->get();
return $query->result();
}
Hope it will be useful for you.
Try this:
function show_last_updated_data(){
$this->db->select('*');
$this->db->from('my_table');
$this->db->group_by('scheme_name');
$this->db->order_by('updated_on', 'DESC');
$query = $this->db->get();
return $query->result();
}
You need to use group by it will retrieve your record based on scheme_code without repeating it and your desire results. :)
function show_last_updated_data() {
$query = $this->db->select('*')
->from('my_table')
->group_by('scheme_code')
->order_by('updated_on', 'DESC')
->get();
return $query->result();
}
As you have used custom date format, so the ordering will not work correctly until you convert the string to date format. Try this:
function show_last_updated_data(){
$this->db->select('*, str_to_date(updated_on, "%Y-%M-%d") date');
$this->db->from('my_table');
$this->db->order_by('date', 'DESC');
$query = $this->db->get();
return $query->result();
}
Note: It is recommended to use native date/datetime field, do not use custom formats. You can also go for UNIX timestamp and save it in an int field. As the date can be fetched in any format afterwards and will save you hassle like this one.

mysql array selecting and sum values

I own an array that three records containing:
value datetime
2 03/03/2015 14:34:00
4 03/03/2015 14:36:00
5 03/03/2015 13:34:00
I want to select the records that are on time 14 and sum them. In the above example would be 4 + 2 = 6
How can I do this?
$sql ="SELECT amperagem, data FROM tomada WHERE date(data) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)";//
mysql_select_db('localiza');
$retval = mysql_query( $sql, $conn );
$num_rows = mysql_num_rows($retval);
while($row = mysql_fetch_array($retval, MYSQL_BOTH)){
$hour= substr(($row['data']),11, 2);
The sql is as simple as:
select sum(value) from tomada where hour(datetime) = 14;
You can actually get yourself the value through a single SQL query:
Be sure to read up on http://www.w3schools.com/sql/func_datepart.asp
So to get all records with a datetime containing 14:XX:XX
SELECT value FROM tomada WHERE HOUR(data) = 14;
Now simply get the rows like you did and retrieve the 'value' per row and add them up
$res = mysql_query( $sql, $conn );
$returnObjects = array();
if ($res == null) {
return $returnObjects;
}
while(!is_null($row = mysql_fetch_row($res))){
$returnObjects[] = $row;
}
$returnArray = mysql_fetch_array($returnObjects);
$sum = 0;
for($row = 0, $size = count($returnArray); $row < $size; $row++){
$sum += $returnArray[$row][0]; //Note 0 is the value you need
}
return $sum;
Note it can be done in less lines of codes with less steps but I find this helps reading what i'm doing. Also some additional checks if certain objects are NULL or values are invalid is recommended.

order and limit on a select query zend framework 2

Earlier this day a asked a question about an update query. But now i want to select some things ( and it is working ) but I also want to order them and put a limit on it.
This is the code to select all the food :
public function getFood($id)
{
$id = (int)$id;
$rowset = $this->tableGateway->select(array('kindOfFood_id' => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
return $row;
}
But how can i do this :
Select * from KindOfFood ==> order by kindOfFood_votes DESC ?
I saw on the documentation you can do something like this, but it doesn't work with me?
$rowset = $artistTable->select(function (Select $select) {
$select->where->like('name', 'Brit%');
$select->order('name ASC')->limit(2);
});
Are you looking to return only single row or multiple rows.
Try this for multiple rows -
use Zend\Db\Sql\Select; //at the top of the page among other use statements.
public function getFood($id)
{
$id = (int) $id;
$select = new Select(TABLE_NAME); //CHANGE TABLE_NAME as per needs
$select->where('kindOfFood_id = ' . $id);
$select->order('kindOfFood_votes DESC');
$resultSet = $this->tableGateway->selectWith($select); //Will get array of rows.
//$row = $rowset->current(); THIS IS FOR RETURNING ONLY SINGLE ROW NOT ALL ROWS
if (!$resultSet) {
throw new \Exception("Could not find rows with food id - $id");
}
return $resultSet;
}
Can access the returned resultSet via loop. Eg: foreach
foreach($resultSet as $row) {
echo $row->kindOfFood_id; //or something
}
Note:
If you need only
Select * from KindOfFood order by kindOfFood_votes DESC
then remove the $select->where('kindOfFood_id = ' . $id); line from above.

How to retrieve every combination of numbers from 2 columns

I'm trying to find a way to retrieve every combination of values from two columns in a table, where each combination matches a value in a third column.
Say part of the table looks like this:
products_id options_id options_values_id
1487 2 1
1487 2 61
1487 3 60
1487 5 52
My desired output, when working with products_id 1487, would be the following two strings:
2-1, 3-60, 5-52
2-61, 3-60, 5-52
I've got the impression that those strings would need to be assembled recursively, but I ran into trouble trying it that way because not every products_id has the same options_ids, or the same number of them.
Edited to add: I've tried variations of a couple of the solutions below, but to no avail. I think I should have been more descriptive.
I'm trying to have it retrieve every combination of unique options_id and its corresponding options_values_id. (In other words, not every single possible combination of numbers from those two columns.) Options_id represents product options like "color" and "size," and options_values_id represents choices of those options, like "red" or "small." So I'm trying to come up with every possible combination of options for a given products_id. In the example above, there are two possible option combinations for that item-- "2-1, 3-60, 5-52" and "2-61, 3-60, 5-52".
Join the table against itself for each distinct option.
Do a select first to retrieve the number of options.
$tables = array();
$rs = mysql_query(
'SELECT DISTINCT options_id FROM table WHERE products_id = '.$id);
while ($row = mysql_fetch_row($rs)) {
$tables[$row['options_id']] =
'SELECT options_values_id FROM table WHERE products_id = '.$id.
' AND options_id = '.$row['options_id'];
}
mysql_free_result($rs);
Then, for each option, join it in as a separate table in your query. Do not include any joining clauses comparing values, just join every record against every other record.
$sql = 'SELECT ';
$count = 0;
foreach ($tables AS $id => $query) {
if ($count++) $sql .= ', ;
$sql .= 'table_'.$id.'.options_values_id AS value_'.$id;
}
$sql .= ' FROM ';
$count = 0;
foreach ($tables AS $id => $query) {
if ($count++) $sql .= ', ';
$sql .= '('.$query.') AS table_'.$id;
}
Finally, execute that query. Each row will contain one column per options_id. There will be one row per unique combination of values.
or for a mixed, php/sql approach, try using that SQL query:
SELECT products_id, options_id, options_values_id WHERE products_id = '$var_with_product_id';
fetch the results into an array, say $results:
$pairs = array();
foreach($results as $result) {
// build array with pairs (array_push to avoid overwriting)
array_push($pairs, array( $result['options_id'] => $result['options_values_id'];
}
// a bit extra complication, as array_push adds e.g. [0] => array( options_id => options_values_id ) :)
$pairs = array_values($pairs);
// check for double options_id
$found_double_options_id = false;
do {
// check for duplicates... use a lot of array functions
} while (count($pairs) && $found_double_options_id);
"Every combination" is the Cartesian product:
SELECT DISTINCT e1.options_id, e2.options_values_id
FROM Entity e1, Entity e2
WHERE e1.products_id = 1487 AND e2.products_id=1487

foreign keys are not being created

Question:
Want to perform a Select Query below (must be this query):
SELECT QuestionId FROM Question WHERE (QuestionNo = ? AND SessionId =
?)
In order to be able to find the QuestionId's in the Question table and
store it in the Answer Table for all the answers so that we can
determine which answers belong to which question
Problem:
The problem with the mysqli code is that it is not able to insert the correct QuestionId value. It keeps displaying 0 for QuestionId in the Answer Table. So can somebody fix this in order to be able to be able to display the correct QuestionId?
It has to be done the SELECT query provided at top. I have to use that in mysqli.
Here are the db tables:
Question Table
QuestionId (auto) SessionId QuestionNo
4 2 1
5 2 2
6 2 3
Answer Table at moment:
AnswerId (auto) QuestionId Answer
7 0 A
8 0 C
9 0 A
10 0 B
11 0 True
What Answer Table should look like:
AnswerId (auto) QuestionId Answer
7 4 A
8 4 C
9 5 A
10 5 B
11 6 True
Below is the code:
$questionsql = "INSERT INTO Question (SessionId, QuestionNo)
VALUES (?, ?)";
if (!$insert = $mysqli->prepare($questionsql)) {
// Handle errors with prepare operation here
echo __LINE__.': '.$mysqli->error;
}
$answersql = "INSERT INTO Answer (QuestionId, Answer)
VALUES (?, ?)";
if (!$insertanswer = $mysqli->prepare($answersql)) {
// Handle errors with prepare operation here
echo __LINE__.': '.$mysqli->error;
}
//make sure both prepared statements succeeded before proceeding
if( $insert && $insertanswer)
{
$sessid = $_SESSION['id'] . ($_SESSION['initial_count'] > 1 ? $_SESSION['sessionCount'] : '');
$c = count($_POST['numQuestion']);
for($i = 0; $i < $c; $i++ )
{
$insert->bind_param("ii", $sessionid, $_POST['numQuestion'][$i]);
$insert->execute();
if ($insert->errno)
{
// Handle query error here
echo __LINE__.': '.$insert->error;
break 1;
}
}
$results = $_POST['value'];
foreach($results as $id => $value)
{
$answer = $value;
$lastID = $id;
$questionidquery = "SELECT QuestionId FROM Question WHERE (QuestionNo = ? AND SessionId = ?)";
if (!$questionidstmt = $mysqli->prepare($questionidquery)) {
// Handle errors with prepare operation here
echo __LINE__.': '.$mysqli->error;
}
// Bind parameter for statement
$questionidstmt->bind_param("ii", $lastID, $sessionId);
// Execute the statement
$questionidstmt->execute();
if ($questionidstmt->errno)
{
// Handle query error here
echo __LINE__.': '.$questionidstmt->error;
break 2;
}
// This is what matters. With MySQLi you have to bind result fields to
// variables before calling fetch()
$questionidstmt->bind_result($quesid);
// This populates $optionid
$questionidstmt->fetch();
$questionidstmt->close();
foreach($value as $answer)
{
$insertanswer->bind_param("is", $quesid, $answer);
$insertanswer->execute();
if ($insertanswer->errno) {
// Handle query error here
echo __LINE__.': '.$insertanswer->error;
break 3;
}
}
}
//close your statements at the end
$insertanswer->close();
$insert->close();
}
?>
You need to retrieve the last value of the sequence used as the autoincrementing id in Question directly after the INSERT - do this using the LAST_INSERT_ID() SQL function. You can then use this value as a parameter when you insert into Answer.
This is an article on how this may be done.
You can also restructure your code by eliminating the query for the question id in the insert-answer loop. Instead, when you insert the questions, fill an associative array with the QuestionId for each QuestionNo. While looping over the answers, use the associative array to quickly retrieve the QuestionId. Memory should not be a concert as you currently have all questions and answers in the HTTP request.
The core of the code will then look like:
// AA: Declare an empty associative array for QuestionNo -> QuestionID
$question_ids = array()
for($i = 0; $i < $c; $i++ )
{
// AA: Extract the QuestionNo for multiple use
$questionNo = $_POST['numQuestion'][$i];
$insert->bind_param("ii", $sessionid, $questionNo);
$insert->execute();
if ($insert->errno)
{
// Handle query error here
echo __LINE__.': '.$insert->error;
break 1;
}
// AA: Retrieve the questionId from MySQL
$questionId = mysql_insert_id();
// AA: Add a key-value pair (QuestionNo, QuestionId) to $question_ids
$question_ids[$questionNo] = $questionId;
}
$results = $_POST['value'];
foreach($results as $id => $value)
{
$answer = $value;
// AA: Look up the QuestionId for the question number
$quesid = $question_ids[$id];
foreach($value as $answer)
{
$insertanswer->bind_param("is", $quesid, $answer);
$insertanswer->execute();
if ($insertanswer->errno) {
// Handle query error here
echo __LINE__.': '.$insertanswer->error;
break 3;
}
}
}
NOTE: I'm not a PHP programmer, and I haven't tested this, so there may be syntax errors. Sorry :(