Ranking Values in Multidimensional PHP Array - php

I've set-up a multidimensional PHP array from a SQL query which pulls back parcels despatched per item on a daily basis.
e.g.
Item|Day1|Day2 |Day3|Day4|Day5
1 |100 | 120 | 90 |150 |60
2 |150 | 200 | 80 |90 |100
3 | 1 | 2 | 3 | 4 | 5
I want to be able to assign a ranked value to each day based upon the amount of items sent out each day and put that either into the existing array, or create a new array to which it can reference
e.g. Item 1 Day 1 = 2, Item 2 Day 1 = 1, Item 3 Day 1 = 3, Item 1 Day 2 = 2 etc etc
I'm new to working with arrays, can anyone recommend a way to do this?

Can you not add an ORDER BY in your query in order to do this for you? That said, this does depend on your database design.
Otherwise if you must do it in the PHP side, you could always make a list one for each day, and when you read the information in from the database you could place it where it belongs. This process will be slower than if you use the database.

Related

Updating Database From Static File JSON Feed

I have a PHP script pulling a JSON file that is static and updates every 10 seconds. It has details about some events that happen and it just adds to the top of the JSON file. I then insert them into a MySQL database.
Because I have to pull every event every time I pull the file, I will only be inserting new events. The easy way would be to search for the event in the database (primary keys are not the same), but I am talking about ~4000 events every day, and I do not want that many queries just to see if it exists.
I am aware of INSERT IGNORE, but it looks like it only uses PRIMARY_KEY to do this.
What can I do (preferably easily) to prevent duplicates on two keys?
Example:
I have a table events with the following columns:
ID (irrelevant, really)
event_id (that I need to store from the source)
action_id (many action_ids belong to one event_id)
timestamp
whatever...
And my data is my JSON comes out on the first pull like this:
event_id|action_id|...
1 | 1
1 | 2
1 | 3
2 | 1
2 | 2
2 | 3
Then the next pull is this:
event_id|action_id|...
1 | 1
1 | 2
1 | 3
1** | 4**
1** | 5**
2 | 1
2 | 2
2 | 3
2** | 4**
I only want the rows marked with asterisks to be inserted, and the others to be ignored. Remember, primary_key column id is completely in this table, and I just use it for ubiquity.
What command can I use to "INSERT" every event I pull, but ONLY adding those that aren't duplicated by way of the two columns event_id and action_id.
Thanks.
Create a unique index of both columns.
CREATE
UNIQUE INDEX event_action
ON tablename (event_id, action_id)

Calculate price of rental period

I am working on a project for which I need to calculate prices of holiday homes available in a selected rental period. I need some help with building a SQL query that combines the following tables and convert the data into an output containing the price for the requested period for each house. It should contain the stay costs, and the additional cost types together with the amount the renter should pay for every cost_type.
I have a table costprofiles which enables the house owner to have multiple prices throughout the year:
+----------------+----------+--------------+
| costprofile_id | house_id | profile_name |
+----------------+----------+--------------+
| 1 | 312 | summer |
+----------------+----------+--------------+
| 2 | 312 | winter |
+----------------+----------+--------------+
I have a table called costprofile_items which is linked to a costprofile via the foreign key costprofile_id. This table contains all different amounts a renter should pay to the owner if the price of the selected period uses this cost_type. Each additional amount can be calculated in four different ways:
per night
per stay
per person
per person per night
The way each amount contributes to the total rent price is stored in the calculation_type column. This is what the costprofile_items table looks like:
+---------------------+----------------+--------+-------------+----------------------+
| costprofile_item_id | costprofile_id | amount | cost_type | calculation_type |
+---------------------+----------------+--------+-------------+----------------------+
| 1 | 1 | 20 | usage_cost | per_night |
+---------------------+----------------+--------+-------------+----------------------+
| 2 | 1 | 8.5 | cleaning | per_stay |
+---------------------+----------------+--------+-------------+----------------------+
| 3 | 1 | 0.82 | tourist_tax | per_person_per_night |
+---------------------+----------------+--------+-------------+----------------------+
I also have the table prices in which each row represents a price per night that can be used between the start_date and the end_date (the weekday of the start_date equals the weekday of arrival at the house and the weekday of end_date equals the weekday of departure). The row also contains a column nights that determines how long a sub period needs to be in order to use this price. This is what the table looks like:
+----------+----------+----------------+------------+------------+-----------+--------+
| price_id | house_id | costprofile_id | start_date | end_date | per_night | nights |
+----------+----------+----------------+------------+------------+-----------+--------+
| 1 | 1 | 1 | 2014-08-04 | 2014-12-01 | 60 | 7 |
+----------+----------+----------------+------------+------------+-----------+--------+
| 2 | 1 | 1 | 2014-08-08 | 2014-12-05 | 70 | 3 |
+----------+----------+----------------+------------+------------+-----------+--------+
| 3 | 1 | 2 | 2014-12-01 | 2015-03-02 | 0 | 1 |
+----------+----------+----------------+------------+------------+-----------+--------+
In the table you can see that for the given house you can book the period from 8 till 11 August and this will cost 3*70 = €210 for the stay. If you are with 4 persons the additional costs are 3*20 = €60 for electricity/gas usage, €8.5 for cleaning and 0.82*4*3 = €9.84 for tourist tax. So the total cost of your weekend will be €288.34. It also should be possible to combine this weekend with for example 2 times the weekly price as described in the first row of the table. In this case the price from 8 till 25 August would be 288.34 + 2*582.96 = €1454.26. Note that the calculation types per_stay and per_person only need to be selected from the first sub period, so the cleaning in the last example is only paid once.
The last table I use for calculating prices is the table prices_per_group. This table is connected to prices via the foreign key price_id. In the prices table above you can see in the last row that the price per night equals 0. In that case the owner had given a price per night for every number of persons that he accepts in his house during this period this price is active. This is the way those different prices are stored:
+--------------------+----------+------------+-----------+
| price_per_group_id | price_id | group_size | per_night |
+--------------------+----------+------------+-----------+
| 1 | 3 | 5 | 50 |
+--------------------+----------+------------+-----------+
| 2 | 3 | 4 | 45 |
+--------------------+----------+------------+-----------+
As you can see a week starting at 1 December (or any Monday after that, but before 2 March) will cost €50 per night if you are with 5 persons or €45 if you are with 4.
I hope it is clear now how I am trying to store and compute all different prices.
I have managed to get these calculations working, by first querying all cost types of every available house with the following query:
SELECT * FROM (
SELECT prices.house_id,
prices.price_id,
prices.costprofile_id,
prices.nights,
prices.start_date,
prices.end_date,
MIN(
prices.per_night + COALESCE(prices_per_group.per_night, 0)
) AS per_night /* Add the price per night from prices and prices_per_group (if one has a non-zero value the other is always zero) */
FROM prices
LEFT JOIN prices_per_group ON prices.price_id = prices_per_group.price_id
WHERE prices.house_id IN (
/* Query that returns a set with the ids of all available houses here */
)
AND (
prices_per_group.price_id IS NULL OR /* If true, no row in prices_per_group is pointing to the price_id currently being evaluated */
prices_per_group.group_size >= 4 /* If true, the group_size satisfies the requested number of persons */
)
GROUP BY prices.price_id
) AS possible_prices
INNER JOIN costprofile_items ON costprofile_items.costprofile_id = possible_prices.costprofile_id
ORDER BY price_id ASC
After that I used PHP to loop through all rows containing price information for a certain house. I started at the start_date and made steps using the first usable price row it could find and repeated that until I am at the end_date. The problem with my current method is that it is too slow. For 1000 houses the webserver needs 0.3sec execution time. Maybe some optimization can be done in my PHP code, but I was hoping someone could help me with putting this all together in SQL. This way for example sorting by price is easier to implement and just asking for the large result after quickly executing the above query makes my execution time jump up to 0.12sec.
All help and advice is welcome
In the end I decided to cache all prices instead of live computing them. This results in much better performance and allows for much more complex pricing than can be computed on the fly inside queries. Every night a cronjob runs that fills up 21 tables (a table for each possible rental duration). The duration pricing tables contain key,value pairs of arrival date and corresponding computed price for that duration. Optionally you can add a column for group size, resulting in a price per duration, per group size, per arrival date. It takes quite some database records, but if you create indices this is blazing fast.

Fixture generator in PHP or MySQL

I am trying to generate fixtures for a sports website. I have a table called Members, with relevant columns being member_id and league_id. The league_id will be passed from a form on the previous page as the variable $leagueid.
I'm pulling out all the member ID's relating to that league ID using...
$result = mysql_query("SELECT member_id FROM Members WHERE league_id = '$leagueid'")
I now need to generate fixtures for all these member ID's and then insert that data into the MySQL table 'Fixtures'. Each row of data in that table needs to include:-
player1 - member_id of the first player
player2 - member_id of the second player
week - integer showing which week the match will be played
league_id
However, there are some special conditions that also need to be applied.
Every 3rd week needs to remain free (i.e. weeks 3,6,9,12 etc). No matches can be scheduled on these weeks
There needs to be an option (which will be selected and passed from a form on the previous page as a checkbox variable called $double) which will double up the matches. This means that after generating one complete round of fixtures, you need to take the generated list, swap ID's for players 1 and 2, and duplicate them all. So 1 round of fixtures could look like this....
Week 1
1 vs 2
3 vs 4
Week 2
1 vs 3
2 vs 4
Week 3
1 vs 4
2 vs 3
Then you would swap the ID's and add on another set...
Week 4
2 vs 1
4 vs 3
Week 5
3 vs 1
4 vs 2
Week 6
4 vs 1
3 vs 2
What I'm looking for is some code that will generate all these fixtures while keeping in mind all the special conditions that I've listed.
I know this is all possible in PHP but I also think I can do it using one SQL query instead, which might be a lot cleaner. Can anyone help me out?? Thanks!!
P.S. I know I'm using mysql and not mysqli. I am currently trying to transfer over to mysqli but I'm having some problems which I have posted on a separate question that I have yet to get a correct answer to.
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
SELECT * FROM ints WHERE MOD(i,3) > 0;
+---+
| i |
+---+
| 1 |
| 2 |
| 4 |
| 5 |
| 7 |
| 8 |
+---+
The second part is simply
INSERT INTO my_table SELECT week_id+3, column_2, column_1 FROM my_table;

Mysql Database design structure

Which is a better db design when inserting records?
**Option 1.**
uid|itemid| qty | price | amount
1 |13| 2 | 100 |200
**Option 2.**
uid| itemid| qty | price | amount
1 |13| 1 | 100 | 100
2 |13| 1 | 100 | 100
In option 2 then retrieve rows via mysql query and calculate using id ...
It seems like you want to retain the original price of each item ordered. What I'd do is go with option 1, then create another column in your table that holds a string that identifies each item in the order.
For example, if I were selling fruit, a banana could have iZ as a unique key. And an apple could have 6U as a unique key. When a user places an order, these unique keys would be inserted into your new column. So when you see this column's data is iZ6U you'd know that the user ordered an apple and a banana.

How should I store weekly poll data?

I'm writing a top 10 polling system. Pollsters vote weekly on their top 10. How should I store their poll for each week? That is, how do I control what week the poll is in storage (mySQL) or in my PHP (5.x+) calculations?
(System #1) I've previously done this by having a file "week.txt" on the server that I set at 0 and then ran a cron job weekly to update +1. When I'm storing the poll data in the database, I'd just load the file and know what week it was. I'm looking for something more elegant.
The system must:
Be able to start at any time of the year.
Be able to skip weeks.
Not require shuffling of week numbers during calculations.
Be maintenance free by a human, other than a 1-off event (like saying "this is the start date", "this is the end date", once in a blue moon).
Use PHP, mySQL, file or other "standard" server items (except other programming languages or databases).
Not require other software (e.g. "Install Software X, it does this!").
Pollsters are probably non-technical people, so asking them anything other than "Enter your top 10" or "edit your top 10" is not allowed.
Be able to go over the end of the calendar year smoothly (e.g. Start in November and end in March).
Other Information:
Pollsters will only be allowed to vote on a single day.
I'll be running multiple polls at once that have no bearing on each other and thus may have different skip weeks.
My system I've used before won't work because in order to skip weeks, it would need interaction and violate #4 and otherwise can't skip weeks and thus violate #2.
I've thought of 2 systems but they have failures of parts of the above:
(System #2) Use PHP's date("W") when the pollster votes. Thus, the first week they all get week #48 (for example), second week #49, so it would be easily to tell which week is what. The problem is that some polls will go over the calendar year, thus I would end up with 48, 49, 50, 51, 52, 1, 2, 3, 4 and violate #3 above. Also, if we skipped weeks, we could end up with 48, 49, 50, 1, 2, 3 which violates #2 and #8 above.
(System #3) Then, I had the idea to just store the date they enter the poll. I would set a date to calculate from the week prior to the first poll, thus, it would just need to calculate the difference between weeks and I'd know the week number. But there's no easy way to skip weeks violating #2 unless we shuffle days which violates #3.
(System #4) I then had the idea that when a pollster first votes, we just record it as their week 1 vote. When they next vote, it's week 2, and so forth. If they wanted to edit their poll (the same day), they'd just use the edit button and we wouldn't record a new poll, because they'd have signaled it's an edit. The only problem is if a pollster forgets a week, meaning I'd have to go in and correct the data (add a blank week or change the week number they voted but violate #4). This handles the skip weeks just fine. Maybe a cron job would solve this? If someone forgot, a cron job that runs after the poll closes would enter in a blank week. Could be programmed to see the max week number entered, if any userid didn't have that week number, just enter in blank data.
If you can adapt any system above to meet all the criteria, that would be fine as well. I'm looking for a simple and elegant and hands-free solution.
Please ask for any other clarifying information.
When working with week numbers, you should keep in mind that 01.01.2012 is in week 52 (not 1). The question is if you want your polls to be fixed on calendar weeks, or 7-day-offsets from the poll-start-date. Consider your poll started on a friday and ended exactly 7 days after. You'd be crossing the calendar week barrier and thus have 2 "weeks" your users may vote.
I'd probably prefer the offset-approach, as strict calendar binding is usually not helpful anyways. Do you want to answer the question "what are the votes in calendar week 34" or "what are the votes in the third week of polling"?
Calculating the offset is quite simple:
// 0-based
$week_offset = floor(time() - strtotime("2011-11-02") / 7);
I don't know your polling algorithm. I'll just demonstrate with a weighted poll (1-3 stars, 3 being best):
| poll_id | user_id | week_offset | vote |
| 7 | 3 | 0 | 1 |
| 7 | 4 | 0 | 3 |
| 7 | 5 | 0 | 2 |
| 7 | 3 | 1 | 2 |
| 7 | 4 | 1 | 2 |
| 7 | 5 | 2 | 3 |
| 7 | 5 | 5 | 1 |
Running a query like
SELECT
poll_id,
week_offset,
SUM(vote) as `value`,
COUNT(user_id) as `count`,
AVG(vote) as `average`
FROM votes_table
WHERE poll_id = 7
GROUP BY poll_id, week_offset
ORDER BY poll_id, week_offset;
would give you something like
| poll_id | week_offset | value | count | average |
| 7 | 0 | 6 | 3 | 2 |
| 7 | 1 | 4 | 2 | 2 |
| 7 | 2 | 3 | 1 | 3 |
| 7 | 5 | 1 | 1 | 1 |
By now you'll probably have noticed the gap 0, 1, 2, [3], [4], 5.
When grabbing that data from MySQL you have to iterate the results anyways. So where's the problem extending that loop for a gap-filler?
<?php
// your database accessor of heart (mine is PDO)
$query = $pdo->query($above_statement);
$results = array();
$previous_offset = 0;
foreach ($query as $row) {
// calculate offset distance
$diff = $row['week_offset'] - $previous_offset;
// make sure we start at 0 offset
if ($previous_offset === 0 && $row['week_offset'] > 0) {
$diff++;
}
// if distance is greater than a single step, fill the gaps
for (; $diff > 1; $i--) {
$results[] = array(
'value' => 0,
'count' => 0,
'average' => 0,
);
}
// add data from db
$results[] = array(
'value' => $row['value'],
'count' => $row['count'],
'average' => $row['average'],
);
// remember where we were
$previous_offset = $row['week_offset'];
}
// 0 based list of voting weeks, enjoy
var_dump($results);
You might also be able to do the above right in MySQL using a function.

Categories