Identify change in job code from a mySQL table - php

I am working with a very large table in MySQL containing employee information collected over the last four years. I want to know if someone's job code changed between that time, and if so, push their data into an array for json encoding in php.
An example of the data looks like this:
Year Emp ID Job Code
2015 1234 X908
2014 1234 X908
2013 1234 X908
**2015 5421 Y444**
2014 5421 Z900
2013 5421 Z900
For employee 1234 there has been no job change between 2013-2015; I would however like to catch employee 5421 whose job code changed between 2014 and 2015.
So far, I have written a script in PHP without good results.
$query = mysqli_query("SELECT year, emp_id, job_code from big_table ");
$rows = array();
while ($r = mysql_fetch_assoc($query)) {
if ($r['emp_id'] == $r['emp_id'] and $r['job_code'] != $r['job_code']) {
$rows[] = $r;
}
echo json_encode($rows);
Thank you in advance for your help!

You can get the required data in the SQL query without having to pull all user records into PHP. This will be significantly more efficient than processing the data in PHP.
You can get the job code count for each user:
SELECT emp_id, COUNT(DISTINCT job_code) AS diffjobs FROM big_table GROUP BY emp_id
Then you can conditionally get users with more than 1 distinct job code by wrapping it in a subquery:
SELECT emp_id, diffjobs FROM
(SELECT emp_id, COUNT(DISTINCT job_code) AS diffjobs FROM big_table GROUP BY emp_id) d
WHERE diffjobs > 1

This loop should work:
$rows = array();
$jobChanges = array();
while ($r = mysql_fetch_assoc($query)) {
if (!isset($rows[$r['emp_id']])){
$rows[$r['emp_id']] = $r;
} elseif ($rows[$r['emp_id']]['job_code'] != $r['job_code']) {
// Handle differing job_code...
if (!isset($jobChanges[$r['emp_id'])) {
$jobChanges[$r[emp_id]] = array();
}
// Keep track of all differing job codes per emp_id
// I am not going to go any further than that...
$jobChanges[$r[emp_id]][] = $r;
}
}

You can get the desired employees whose job has changed in between years using GROUP BY and HAVING clause, like this:
$query = mysqli_query($link, "SELECT emp_id from big_table GROUP BY emp_id HAVING COUNT(DISTINCT job_code) > 1");
$rows = array();
while ($r = mysqli_fetch_assoc($query)){
$rows[] = $r;
}
echo json_encode($rows);
Caution: Don't mix mysqli and mysql database extensions in your code.

This can be done with a JOIN query in MySQL, so that you only return the rows that have changed from the previous year -
SELECT
a.year, a.emp_id, a.job_code,
b.year prev_year, b.job_code prev_job_code
FROM big_table a
JOIN big_table b
ON a.emp_id = b.emp_id
AND b.year = a.year-1
WHERE a.job_code != b.job_code
see this SQLFiddle - http://sqlfiddle.com/#!9/95e680/10
so your code could be simplified to -
$query = mysqli_query("SELECT a.year, a.emp_id, a.job_code, b.year prev_year, b.job_code prev_job_code FROM big_table a JOIN big_table b ON a.emp_id = b.emp_id AND b.year = a.year-1 WHERE a.job_code != b.job_code ");
$rows = array();
while ($r = mysqli_fetch_assoc($query)) {
$rows[] = $r;
}
echo json_encode($rows);
note - you had mysqli_query(), but mysql_fetch_assoc(), so I updated to mysqli_fetch_assoc()
If you still wanted to do this in php, you need to save the last row value to a temporary array, and then check if the job_code has changed.
it would look something like this -
$query = mysqli_query("SELECT year, emp_id, job_code from big_table ORDER BY emp_id, year");
$temp = array();
$rows = array();
while ($r = mysqli_fetch_assoc($query)) {
if(!isset($temp[$r['emp_id']]){
$temp[$r['emp_id']] = $r; // add this row to the temp array
}
else {
if ($r['job_code'] != $temp[$r['emp_id']]['job_code']) {
// add the previous values for comparison
$r['prev_year'] = $temp[$r['emp_id']]['year'];
$r['prev_job_code'] = $temp[$r['emp_id']]['job_code'];
// add this row
$rows[] = $r; // add this row
// replace the last temp array with this array
$temp[$r['emp_id']] = $r;
}
}
echo json_encode($rows);
note - I changed the query by adding an ORDER BY -> ORDER BY emp_id, year

Related

To retrieve data from SQL table fields with different IDs into corresponding different variables

The sql database table named 'data' has the fields 'date', 'project_id' and 'amount'. I am able to retrieve the total amount spent on a single project with the code below:
$sql = "SELECT sum(data.amount) AS wn001_amount FROM data WHERE project_id='wn001'";
$result = $link->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$wn001 = $row['wn001_amount'];
echo $wn001;
}
}
But what I want to do is to retrieve the total amount of all projects and store them in different variables like:
$wn001 = total amount entered on different dates for project wn001
$wn002 = total amount entered on different dates for project wn002
$wn003 = total amount entered on different dates for project wn003 etc...
How can I code for that? Find arrays and loops a bit confusing. Thanks.
If you know what the projects are, you can use conditional aggregation:
SELECT SUM(CASE WHEN d.project_id = 'wn001' THEN d.amount END) AS wn001_amount,
SUM(CASE WHEN d.project_id = 'wn002' THEN d.amount END) AS wn002_amount,
SUM(CASE WHEN d.project_id = 'wn003' THEN d.amount END) AS wn003_amount
FROM data d;
You could use GROUP BY, but then you would need to cycle through the result set to assign the appropriate values in your application code.
You should use array for things like that
$sql = "SELECT project_id, sum(data.amount) AS amount FROM data";
$result = $link->query($sql);
$res = [];
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$projectId = $row['project_id'];
$res[ $projectId ] = $row['amount'];
}
}
Now in $res you have:
[
'wn001'=>...,
'wn002'=>...,
...
]

Is there a way to sort PHP array_merge based on raw[2]?

I am creating a time attendance PHP script. I started from storing data in the database MySQL and use different tables for the Punch-in/Punch-out, Breaks, Lunch/Dinner breaks.
Each one of the casualties mentioned before have a table which is structured with an:
ID, start, end, flag
I have already tried to do this with the following code of foreach but unsuccessfully!
foreach ($total as $key => $row) {
$start[$key] = $row['start'];
}
$final = array_multisort($start, SORT_DESC, $total);
echo $final;
Here is the code that I have made with 2 SELECT MySQL queries
$result = mysqli_query($db, "SELECT * FROM time WHERE username =
'$user_check' ORDER BY start DESC");
$data = array();
while ($row = mysqli_fetch_assoc($result)) {
$data[] = $row;
} $db->next_result();
$result = mysqli_query($db, "SELECT * FROM break WHERE username =
'$user_check' ORDER BY start DESC");
$data2 = array();
while ($row = mysqli_fetch_assoc($result)) {
$data2[] = $row;
}
$total = array_merge ($data, $data2);
I would like to have 1 array of this 2 queries sorted DESC by start as I will use this array to populate a table in DESC order.
Use UNION ALL in your query, instead of merge arrays. Let the database do the work for you:
SELECT * FROM
(SELECT * FROM time WHERE username = '$user_check'
UNION ALL
SELECT * FROM break WHERE username = '$user_check'
) ORDER BY start DESC
PS: Your code is vulnerable to SQL Injection. You should use prepared statement instead.

How can I show rows from one table that aren't in another table?

I have two tables in a database, one of them is a list of 'buildings' you could create. The other is a list of buildings that have been built by users.
On one page, (cityproduction.php), it displays a list of 'buildings' you can build.
I want it to display the buildings that you can build, that you haven't already built.
Here is my code:
$sql = "SELECT * FROM [The list of built buildings] WHERE building_owner = '$user'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$variable = $row["building_name"];
}
(...)
$sql = "SELECT * FROM [The list of ALL buildings] WHERE name != '$variable' ORDER BY id asc";
$result = mysqli_query($database,$sql) or die(mysqli_error($database));
while($rws = mysqli_fetch_array($result)){
echo $rws["name"]; (etc.)
What this is doing is only not-showing one of the buildings that the user has built, not all of them.
Without seeing the real table names or the schema it is tricky to answer accurately but you could try something along these lines:
SELECT * FROM `all_buildings`
WHERE `id` not in (
select `building_id` from `built_buildings` where `building_owner` = '$user'
)
ORDER BY `id` asc;
Another translation of your question into SQL (besides NOT IN) results in a Correlated Subquery:
SELECT * FROM `all_buildings` AS a
WHERE NOT EXISTS
(
select * from `built_buildings` AS b
where a.`id` = b.`building_id` -- Outer Select correlated to Inner
and b.`building_owner` = '$user'
)
ORDER BY `id` asc;
The main advantage over NOT IN: it's using only two-valued-logic (NULL is ignored = false) while NOT IN uses three-valued-logic (comparison to NULL returns unknown which might no return what you expect)
Why are you using while after the first query, it suppose to be a list or just a single value? because if you use $variable in your second query it will only have the value of the last value of the list you are getting
if ($result->num_rows > 0) {
$variable = array();
while($row = $result->fetch_assoc()) {
$variable[] = $row["building_name"];
}
Second query example:
foreach($variable as $building) {
$sql = "SELECT * FROM [The list of ALL buildings] WHERE name != '$building' ORDER BY id asc";
$result = mysqli_query($database,$sql) or die(mysqli_error($database));
$result = mysqli_fetch_assoc($result);
echo $result["name"];
}
Assuming both of your tables have some sort of id column to relate them, with this query:
SELECT building_name, building_owner FROM
test.all_buildings a
LEFT JOIN test.built_buildings b ON a.id = b.building_id AND b.building_owner = ?
ORDER BY building_owner DESC, building_name;
(where ? is the user), you can select all the buildings, first the ones that have been built, followed by the ones that haven't, in one query. If your tables don't have id's like that, you can join them on name instead; it should work as long as the names are distinct.
Then as you fetch the rows, you can sort them into "built" or "not built" by checking if the row has a building_owner.
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
if ($row['building_owner']) {
$built[] = $row['building_name'];
} else {
$not_built = $row['building_name'];
}
}
}

Combining 6 queries into 1 resultset for exporting?

Ok, so this may sound a little strange and maybe over complicated. Here is the situation. I 2 sets of 3 queries. I will try to make a simple example to explain exactly what I am trying to do:
Queries:
//First set of queries
$query1 = "SELECT Name, Date FROM Table1";
$query2 = "SELECT Type, Place, Location FROM Table2";
$query3 = "SELECT One FROM Table3";
//Second set of queries
$query4 = "SELECT Name, Date FROM Table1 WHERE ID=1";
$query5 = "SELECT Type, Place, Location FROM Table2 WHERE ID=1";
$query6 = "SELECT One FROM Table3 WHERE ID=1";
You just have to trust me when I tell you that I CANNOT combine these two sets of queries. these are over simplified select statements to get the concept of what I am trying to do.
So here is my php code:
//Set 1
$data1 = mysql_query($query1) or die(mysql_error());
$data2 = mysql_query($query2) or die(mysql_error());
$data3 = mysql_query($query3) or die(mysql_error());
while ($line1 = mysql_fetch_array($data1, MYSQL_ASSOC) &&
$line2 = mysql_fetch_array($data2, MYSQL_ASSOC)) {
while ($line3 = mysql_fetch_array($data3, MYSQL_ASSOC)) {
//COMBINE $line1, line2, line3 into a single $lineSet1 -- HOW DO I DO THIS?
}
}
//Set 2
$data4 = mysql_query($query4) or die(mysql_error());
$data5 = mysql_query($query5) or die(mysql_error());
$data6 = mysql_query($query6) or die(mysql_error());
while ($line4 = mysql_fetch_array($data4, MYSQL_ASSOC) &&
$line5 = mysql_fetch_array($data5, MYSQL_ASSOC)) {
while ($line6 = mysql_fetch_array($data6, MYSQL_ASSOC)) {
//COMBINE $line4, line5, line6 into a single $lineSet2 -- HOW DO I DO THIS?
}
}
//Append $lineset1 and $lineset2 so I have 1 resultset $results
$result = array_merge($lineSet1, $lineSet2);
//So now I can pass this $result array into my array2csv function that takes a multidimensional array:
function array2csv(array &$array)
{
if (count($array) == 0) {
return null;
}
ob_start();
$df = fopen("exportedLeads{$_SESSION['id']}.csv", 'w');
fputcsv($df, array_keys(reset($array)));
foreach ($array as $row) {
fputcsv($df, $row);
}
fclose($df);
return ob_get_clean();
}
I know this seems really complicated, but I am pretty confused and not that good at php. Any help would be appreciated. Thanks!
TABLE1:
ID | Name | Date
TABLE2:
ID | Table1_ID | Type | Place | Location
TABLE3:
ID | Table1_ID | One
EDIT: I have been reading into JOIN statements. Is this possible a case for that?
You can resume both of your sets into a single query like using JOIN assuming your ID's match.
First set into 1 query:
SELECT t1.Name, t1.Date, t2.Type, t2.Place, t3.One FROM Table1 t1
JOIN Table2 t2
ON t2.Table1_ID = t1.ID
JOIN Table3 t3
ON t3.Table1_ID = t1.ID
Second set into 1 query:
SELECT t1.Name, t1.Date, t2.Type, t2.Place, T2.Location, t3.One
FROM Table1 t1
JOIN Table2 t2
ON t2.Table1_ID = t1.ID
JOIN Table3 t3
ON t3.Table1_ID = t1.ID
WHERE t1.ID = 1
Assuming your three queries all return the SAME number of rows, you can abuse the assignment operator:
while(($row1 = mysql_fetch($result1))
&& ($row2 = mysql_fetch($result2))
&& ($row3 = mysql_fetch($result3))) {
to fetch from all three result sets in parallel. The downside is that if your result sets are different lengths, you'll only fetch as many records as there are in the SHORTEST result set. e.g. as soon as any of those fetch calls returns false, the entire while aborts, even if there's still data in the other two results.

Check if the values of two columns from two tables are equal then return

I have 2 tables as follow :
table1 has these fields:
year,day,month,name
table2 has these fields:
years,gift
I want to return the rows where year = years. the two queries NEED to be run separately that is a condition.
I CANNOT join them into 1 query (I know how to do that but that's not how this should be done). Any suggestions? I thought of using a foreach loop but failed to implement, any ideas?
$sql1 = " SELECT * FROM table1 ORDER BY id ASC";
$result1 = mysql_query($sql1);
$rows = mysql_fetch_array($result1);
while($rows = mysql_fetch_array($result1))
{
$year[] = $rows['year'];
}
$sqlx = "SELECT * FROM table2";
$result = mysql_query($sqlx);
while($row = mysql_fetch_array($result))
{
$years[] = $rows['years'];
}
This is a strange requirement, since it should be done with a join. It is vastly less efficient to do this with multiple queries in a loop than a single query with a JOIN.
In any case if you must do it this way, you have retrieved the years into an array correctly from your first query -- use that array to populate the query for each loop iteration of your second query:
$year = array();
$sql1 = " SELECT * FROM table1";
$result1 = mysql_query($sql1);
if ($result1) {
$rows = mysql_fetch_array($result1);
while($rows = mysql_fetch_array($result1))
{
$year[] = $rows['year'];
}
}
// Will hold your output
$gifts = array();
// $year is now an array of years.
// Loop over it to query table2:
foreach ($year as $y) {
// Make sure it is an integer...
$y = intval($y);
// And query table2 using $y as a WHERE condition
$sql2 = "SELECT years, gift FROM table2 WHERE years = $y";
$result2 = mysql_query($sql2);
if ($result2) {
while($row = mysql_fetch_array($result2)) {
// Append all rows to the $gifts array
$gifts[] = $row;
}
}
}
// They're now all in the array $gifts.
var_dump($gifts);
Update:
years on table 1 is a period in years. year in table is a date of when the account was created. so I have to find the difference from today's date to year on table2. then see if it matches any of the rows on table1. and return the results that match
This can be accomplished with a creative JOIN using date functions. Assuming table2.years is a DATE or DATETIME, use YEAR() to return only the year portion from it, and compare it to YEAR(CURDATE()) for the current year. Join that against the number of years in table1.year.
SELECT
table1.*,
table2.*
FROM
table1
JOIN table2
ON table1.year = (YEAR(CURDATE()) - YEAR(table2.years))

Categories