Fill gaps within an array with previous values - php

I have a table called salary_raise which looks like:
id | employee_id | salary | year | month | date
1 | 1 | 1000 | 2014 | 2 | 2014-01-30
2 | 1 | 1200 | 2015 | 3 | 2015-02-20
3 | 1 | 1300 | 2015 | 4 | 2015-03-29
... and so on for multiple employees. It keeps records only when a raise of salary occurs (and this can occur random over a period of time).
Right now I can extract an array which looks like $salary['year']['month'] = $salary_value:
[2014]
[2] => 1000
[2015]
[3] => 1200
[4] => 1300
I need to extract a full report with the salary in each month up to date (filling automatically the months/years missing), like:
[2014]
[2] => 1000
[3] => 1000
[4] => 1000
[5] => 1000
[6] => 1000
[7] => 1000
[8] => 1000
[9] => 1000
[10] => 1000
[11] => 1000
[12] => 1000
[2015]
[1] => 1000
[2] => 1000
[3] => 1200
[4] => 1300
[5] => 1300
I can't figure it out how to do it by using php and/or mysql.
Many thanks!

In order to 'fill the gaps' you need to source the information somewhere.
Easiest way to do this in mysql is to have some generic calendar tables, which list single values for things like month numbers, years etc.
So given a table months(month), and a table years(year), the former contains 1..12 and the latter contanis 2010..2017 (for example), we can cross join the two of those to give us every month for every year, and then left join the salary_raise table to this.
That will leave us a table with a few entries and a lot of nulls, so then we need to use some variables to populate the null values with the last salary entry.
select `year`, `month`, if(salary is null, #prev_sal, #prev_sal := salary) salary
from (
select `years`.`year`, `months`.`month`, salary
from `years` cross join `months`
left join salary_raise sr
on sr.month = `months`.`month`
and sr.year = `years`.`year`
and sr.employee_id = 1
where (`years`.`year` = 2014 or `years`.`year` = 2015)
order by `years`.`year` asc, `months`.`month` asc
) q cross join (select #salary := null) qq
demo here
Of course you'll need to limit that to not check dates in the future -- that should be pretty easy tho.

Related

How to extract data from column and insert into an array and add the values

Database:
ID | CID | HOURS
1 | 201 | 8, 8, 8, 8
2 | 201 | 2, 4, 7, 5
3 | 201 | 4, 3, 7, 1
How can I extract the values in the HOURS column and add the values to produce the results of on a HTML table:
TOTAL HOURS: 14 | 15 | 22 | 14
I am assuming that the values in Hours column need to be inserted into an array? So, I did a while loop and exploded the results and got the following:
while($row = sqlsrv_fetch_object($result)) {
$hours = explode(', ', $hours)
}
I do a print_r($hours, 1) and get the following:
Array
(
[0] => 8
[1] => 8
[2] => 8
[3] => 8
Array
(
[0] => 2
[1] => 4
[2] => 7
[3] => 5
)
Array
(
[0] => 4
[1] => 3
[2] => 7
[3] => 1
)
How do I loop this to do the calculation?
Setting aside the comma-delimeted data being an anti-pattern, it's possible to return your aggregated values in a single query without needing to loop over anything by using string_split, the basics of which are:
select
Sum(case col when 1 then v end),
Sum(case col when 2 then v end),
Sum(case col when 3 then v end),
Sum(case col when 4 then v end)
from (
select v, row_number() over(partition by id order by id) col
from t
cross apply(
select Cast(value as int)v from String_Split(t.hours,',')
)v
)v
demo Fiddle

Scoring system for collection of numbers (serial#)

I'm looking for a formula (PHP) that I can use to assign a score base on rarity of a number inside a collection. Serial#1 being the rarest.
For example, I have few sets of collections.
Collection 1 - Total number of items = 10 (Serial #1 to Serial #10)
Collection 2 - Total number of items = 100 (Serial #1 to Serial #100)
Collection 3 - Total number of items = 3500 (Serial #1 to Serial #3500)
Based on the 3 example sets. Collection 1 is considered the rarest collection because of only 10 items available in the set.
In addition to this. Each item is assigned with its serials, #1 being the best (rarest)
Here is the table of how I visualize the scoring system.
Collection 1
| Serial#| Score |
|:------:| :-----:|
| 1 | 1000 |
| 2 | 900 |
| 3 | 800 |
| 4 | 700 |
| 5 | 600 |
| 6 | 500 |
| 7 | 400 |
| 8 | 300 |
| 9 | 200 |
| 10 | 100 |
Collection 2
| Serial#| Score |
|:------:| :----:|
| 1 | 800 |
| 2 | 700 |
| 3 | 600 |
| 4 | 500 |
| ... | ... |
| 99 | 12 |
| 100 | 10 |
I just made up those scores just for representation.
With the tables above, Serial #1 in Collection 1 has a higher score compared to Serial #1 in Collection 2 because of the rarity of Collection 1.
I have few collections ranging from 1 of a kind (rarest of them all),10, 20, 150, 350, 1000, 5000, 10000, 50000” What “score” is this item supposed to get then?
for the rarest 1 of 1 the score will be based on the score of the
other Serial #1. If for example Collection with 10 items the serial#
get 1000 points then the 1 of 1 i can give 5000 points (this one no
need to calculate)
2. Are all the scores inside a collection supposed to add up to the same value?
No. It doesn't need to add up to the same value as the other collections
Your “made up” scores aren’t really helpful in explaining what kind of scoring logic you want to apply here in the first place
In the example table (made up scores). I just want to show that the different serial number scores. The higher serial number will have a lower score compare to the lower serial#. But the scores will differ from the other collections with the same serial number. Thus I categorized them by collections. Lower item count collections are considered rarer than those with higher item count.
4. But Serial #1 is supposed to have a higher score, than Serial #2, inside the collection? If so, then what would that be based on? Just the ascending order of those #1, #2, etc.?
Maybe it will be based on the ascending order.
5. All 10 items in collection 1 could get the same score?
No.
I don't have any preference on the score the serial number will get. I mean Serial #1 can get X score as long as it will be relative to the rarity of the collection. Serial #1 score will not be the same across collection unless they belong to the same collection rarity.
If I understand you correctly you want to count occurrences of score (value) and sort that occurrence count in a way that the lowest number (serial) will represent the values (score) that are the rarest and the higher the serial number is the value is more common.
For the:
Input Colleciton:
Array
(
[0] => 5
[1] => 4
[2] => 3
[3] => 2
[4] => 1
[5] => 10
[6] => 10
[7] => 11
[8] => 11
[9] => 12
[10] => 12
[11] => 13
[12] => 13
[13] => 14
[14] => 14
[15] => 100
[16] => 100
[17] => 100
[18] => 101
[19] => 101
[20] => 101
[21] => 102
[22] => 102
[23] => 102
[24] => 103
[25] => 103
[26] => 103
[27] => 104
[28] => 104
[29] => 104
)
this code:
<?php
header('Content-Type: text/plain');
// generate colleciton
$collection = [];
# most unique values
$start = 5;
$end = 0;
for ($i = $start; $i > $end; $i--) {
$collection[] = $i;
}
# less unique values (2x the same values)
$start = 10;
$end = 15;
for ($i = $start; $i < $end; $i++) {
$collection[] = $i;
$collection[] = $i;
}
# least unique values
$start = 100;
$end = 105;
for ($i = $start; $i < $end; $i++) {
$collection[] = $i;
$collection[] = $i;
$collection[] = $i;
}
echo "Input Colleciton:\n";
print_r($collection);
# array of [value => how_many_occurences, ... => ...]
$valueCount = array_count_values($collection);
echo "Value count:\n";
print_r($valueCount);
$uniqueBins = [];
# convert to unique bins
foreach ($valueCount as $value => $key) {
$uniqueBins[$key][] = $value;
}
echo "Unique bins:\n";
print_r($uniqueBins);
// optionally sort by value
foreach ($uniqueBins as $key => $bin) {
asort($bin);
$uniqueBins[$key] = $bin;
}
echo "Unique bins after sort:\n";
print_r($uniqueBins);
generates the output:
Unique bins after sort:
Array
(
[1] => Array
(
[4] => 1
[3] => 2
[2] => 3
[1] => 4
[0] => 5
)
[2] => Array
(
[0] => 10
[1] => 11
[2] => 12
[3] => 13
[4] => 14
)
[3] => Array
(
[0] => 100
[1] => 101
[2] => 102
[3] => 103
[4] => 104
)
)
Where values from the array under the key 1 are rarest and values from the array under the key 3 are most common.
The key number is not starting from zero but from 1 because values that are rarest have only single (1) occurrence in the whole collection.
If you want the output bin array to have keys starting from zero and continues in numbering (0,1,2,...) then use array_values(), this way:
$uniqueBins = array_values($uniqueBins);
at the end. By doing that you loose the information about the count (rarity) of values however you still have arrays of values in the order of their rarity (starting from most to the least)
To make this code work you need to have all values into single array $collection but if you have your values in multiple arrays then you can merge all of them into single $collection by using array_merge
Here are headlines to get you started....
Create a DB
Like this
Category :Rarest
uniqueid item name score
hashed#125 Dragon 1000000
hashed#122 Hydra 800000
hashed#100 Medusa 750000
Category :Rarer
uniqueid item name score
hashed other1 50000
hashed other2 30000
hashed other3 10000
Category :Rare
uniqueid item name score
hashed other1 5000
hashed other2 2000
hashed other3 800
This is how you can approach this...
0.hidden readonly checkbox[contains uniquehash] associate with clickable images[showing name of item]
1.[Connect to DB] mysql here
2.[query from tables (all uniqueids 1,2,3 and...)]
3.[from Post get user inputs as array][say user click image showing medusa and hydra - the hidden checkboxes send hashed#100 and hashed#122]
4.[foreach DB values as #strings]( unique ids are broken to individual strings)
5.[foreach user array as #strings]( uniqueids from userinputs are broken to individual strings)
6.[if user string match DB #strings](match each hash]
7.[return query [score] and [name of item] from where #string is found ](only matching ids return their score in this case score 800000 and 750000 as well as Hydra + Medusa)
8.[then [score for match1]] + [score for match2] + match 3 and so on]
(800000+750000)
echo [total score for user selections] + [names of items]
(1.55mil,hydra + medusa ) you can even show they are from the rarest collection
Since the beginning of the process the user in the frontend does not know what the item score is and there is no way to know they just get the total

Combining data from multiple arrays into a final array for output in CSV

I'm trying to restructure my arrays so that I can get the desired output in one final array that will be used to fill a csv, eventually spreadsheet.
My first msyql query and the resulting array give me this structure:
array(
[0] => Array
(
[sku_id] => 1
[dealer_id] => 1976
[locations] => 1
[groupID] => 1
[frame] => 1051
[cover] => 1150
[color] => 99
[start_date] => 2018-03-
)
[1] => Array
(
[sku_id] => 1
[dealer_id] => 5400
[locations] => 1
[groupID] => 1
[frame] => 1051
[cover] => 1150
[color] => 99
[start_date] => 2017-04-
)
[2] => Array
(
[sku_id] => 1
[dealer_id] => 11316
[locations] => 1
[groupID] => 1
[frame] => 1051
[cover] => 1150
[color] => 99
[start_date] => 2017-02-
)
)
So I get 3 records, each for the same product (sku_id) but a different customer(dealer_id). Then I match this data up in a similar table in DB2 to get quantity for each order of the sku_id. That 2nd query/array shows this:
Array
(
[0] => Array
(
[CSTNOC] => 1976
[TOTALQTY] => 2
)
[1] => Array
(
[CSTNOC] => 5400
[TOTALQTY] => 5
)
[2] => Array
(
[CSTNOC] => 11316
[TOTALQTY] => 14
)
)
However, this is what I'm trying to achieve.
In the below example, sku_id, groupID and location would come directly from the first query. Days is the number of days between start_date (from query 1) and curDate(). Qty is orqtyc from query 2 and Average is a calculation based on locations, days and qty. Each row is based on a different customer, so even though the below example is for one sku, the rows signify a different customer with different locations, qty, etc.
SKU_id | groupID | locations (n) | Days (x) | Qty(q) | Average (x/(q/n))
--------------------------------------------------------------------------------
123 1 3 120 15 24
123 1 2 12 6 4
The Report or CSV would then only show one line item for the above:
SKU | Group | Average Days | Total units sold
123 | 1 | 28 | 21
Basically, for every sku I'm getting those records and then I need to do those calculations by row, and get a total of those values for each sku. The report will only have one line item per sku.
I have everything I need to get there, but how can I structure a final array to get my desired outputs like above?
FULL SCRIPT:
$skuQuery = "
SELECT
sku_id,
dealer_id,
locations,
s.sku_group_id as groupID,
s.frame as frame,
s.cover1 as cover,
s.color1 as color,
start_date - interval 7 day as start_date
from products p
inner join skus s on p.sku_id = s.id
where curdate() between p.start_date and p.expire_date
group by sku_id, dealer_id
limit 3";
$skuRslt = mysqli_query($conn,$skuQuery);
while($skuRow = mysqli_fetch_assoc($skuRslt)){
$skuResult[] = $skuRow;
$dealerQuery = "
SELECT
cstnoc,
sum(orqtyc) as TotalQTY
from table
where cstnoc = {$skuRow['dealer_id']}
AND framec = {$skuRow['frame']}
AND colr1c = {$skuRow['color']}
AND covr1c = {$skuRow['cover']}
AND extd2d >= " . str_replace('-', '', $skuRow['start_date']) . "
group by cstnoc, framec
";
$dealerRslt = odbc_exec($DB2Conn, $dealerQuery);
foreach($skuResult as $skuRow){
while($dealerRow = odbc_fetch_array($dealerRslt)){
$dealerResult[] = $dealerRow;
}
}
print_r($dealerResult);
}

PHP Like System / 'Most Liked' (Need for logic)

I'am trying create a basic comment system, I was success but I have a problem right now.
I can't list "most liked" comments and I don't have an idea about how can I.
My votes database showing like that:
voteid | value | entryid | userid
25 | like | 257 | 17
24 | like | 257 | 17
23 | unlike | 257 | 18
I create a new like with this code:
$vote = $connect->prepare("INSERT INTO votes (entryid, userid, value) VALUES (:entryid, :userid, :value)");
$vote->bindParam(':entryid', $entryid);
$vote->bindParam(':userid', $userid);
$vote->bindParam(':value', $value);
$vote->execute();
And my question.
What's my need SQL Query?
I've tried like this:
$bestliked = $connect->prepare("SELECT * FROM votes");
$best = $bestliked->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_COLUMN, 2);
But I can't list the array which have most subarray. It only seems that;
[1] => Array
(
[0] => 8
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
[6] => 7
[7] => 9
[8] => 10
[9] => 11
[10] => 12
[11] => 13
[12] => 14
[13] => 15
)
[2] => Array
(
[0] => 16
)
[3] => Array
(
[0] => 17
)
Thanks All!
You could try to sum the group count and sort it descending:
Select *,count(*) as sum group by entryid where value='like' order by sum desc

Laravel - update multiple records with different values

I have a table , similar to this:
| key | value |
|----------|-------|
| limit | 15 |
| viplimit | 25 |
| .. | |
And i have an array :
Array
(
[0] => Array
(
[key] => limit
[value] => 10
)
[1] => Array
(
[key] => viplimit
[value] => 99
)
...
Now , saying we have 100 rows. What would be the best way to update the table corresponding to the array ?
There would be the option of a query for each 100 row, but that is just bad performance.
This should work:
$statement = "UPDATE mytable
SET key = CASE id
WHEN 1 THEN 'key'
WHEN 2 THEN 'another_key'
WHEN 3 THEN 'some_key'
END,
value = CASE id
WHEN 1 THEN 15
WHEN 2 THEN 25
WHEN 3 THEN 45
END
WHERE id IN (1, 2, 3)
");
DB::statement($statement);
Just think how to create correct query. If it's admin panel or something that will be run not very often, I'd just use iteration to keep things simple.

Categories