I am attempting to upgrade an Old Custom eCommerce system. I am trying to make a log of quantity changes made to an item based on rows from a table (call it "old_table") that the old custom system creates. I can't seem to be able to crack this problem.
As someone starts to make a change to an Item in the system, it creates a record in the "old_table" with the values:
Time | User | Current Qty | Status | Item
-----------------------------------------------------------
2015-07-09 05:30:47 | Admin | 10 | Start | ABC 1
When the person finishes changing the quantity for an Item in the system, it creates a record in the "old_table" with the values:
Time | User | Current Qty | Status | Item
-----------------------------------------------------------
2015-07-09 05:50:47 | Admin | 09 | End | ABC 1
--- Note: Sadly, if the quantity is set to "00" there will not be an "End" entry. I have guessed that i would set a 24 hour limit then would consider that user to have set it to "00" ---
My Goal
In a new Table, I am trying to create a row stating that the User "Admin" subtracted/added a total of "X" of the Item "ABC 1" and if within 24 hours there is no "End" row within the "old_table" for the given item to make a row stating that the User "Admin" set the value to "00".
Anyone have Ideas, suggestions or a solution? I have tried for weeks to solve this. Is it not possible?
You tagged this with mysql AND sql-server. Which system/version is the one in use?
With SQL-Server (>=2008) you could use window function (OVER clause: https://msdn.microsoft.com/en-us/library/ms189461.aspx) which gives you grouped count, sum, avg and other aggregate function.
Related
Big problem...
I'm implementing an online ticket sale system in PHP and MySQL. I have a table called "block_of_tickets", or something like that...
This table looks like:
+-----------+------------+--------------+--------------+--------------+
| idblock | block_name | total_tickets| block_gender | idblock_pair |
+-----------+------------+--------------+--------------+--------------+
| 1 | Block 1- M | 100 | MALE | 2 |
+-----------+------------+--------------+--------------+--------------+
| 2 | Block 1- F | 100 | FEMALE | 1 |
+-----------+------------+--------------+--------------+--------------+
Where:
idblock: The id (primary key) of the block of tickets.
block_name: The name of the block. In the example I have a "Block 1- M" and "Block 1- F" to represente the "Block 1 - Male" and "Block 1 - Female", respectively.
total_tickets: the total of available tickets
block_gender: the gender of the block of tickets
idblock_pair: the block wich is pair of the current block.
Note: There are also other columns, like "price", etc.
Here is the (big) problem:
When there is a "idblock_pair", it means that both block of tickets will share the same total_tickets (available tickets), so both cells must have exactly the same value in this case. As you can see in the example above, block 1 points to block 2 and vice-versa.
Lots of people buy lots of tickets in (almost) the same time, wich means that each sold ticket must decrement 1 in the "total_tickets" field, for both cells.
Database Normalization can solve this. However, it would lose a lot in performance.
I'm almost sure that I should use "SELECT... FOR UPDATE"... but I don't know how, since it's the same table, and a "deadlock" can occur...
How to solve this problem? Do I have to use Triggers? Proccedures? Do I have to use the PHP processing (and transactions) to solve this?
In the example below, one ticket were sold, and now I'm decrementing the total_tickets by 1:
START TRANSACTION;
SELECT *
FROM block_of_tickets
WHERE idblock in (1,2) FOR UPDATE;
UPDATE block_of_tickets
SET total_tickets = (total_tickets - 1)
WHERE idblock in (1,2);
COMMIT;
Is this a nice solution?
I am designing a system where I have multiple shops where each shop should have its own set of sequential numbers for its invoices. Obviously my primary ID column will be sequential for all invoices in the system so obviously I will need another column to store this "shop specific" invoice number. What is the best manner to store and get the next ID for this shop specific number? For example would it be safe to simply get it right from the invoices table doing something like: SELECT MAX(INV_NUM) FROM INVOICES WHERE SHOP_ID = # and add one, and subsequently create the new invoice record? Or would there be issues with the timing if 2 instances of my script were to run at the same time? For example the first script fetches the next ID, but before it gets the chance to create the new invoice record the second instance requests the next ID and gets the same one as the first script... I was then thinking about just storing the last used number in a separate table and as soon as I request the next invoice number immediately write the new next order number and persist it so that the time between fetching the next order number and creating the record that the next request would rely on is kept to an absolute minimum... literally 3 lines of code:
$nextId = $shop->getLastId() + 1;
$shop->setLastId($nextId);
$em->persist();
Invoices
------------------------------
| ID | INV_NUM | SHOP_ID |
------------------------------
| 1 | 99 | 1 |
| 2 | 100 | 2 |
| 3 | 100 | 1 |
Shops
-------------------
| ID | LAST_ID |
-------------------
| 1 | 100 |
| 2 | 100 |
If you're using Doctrine, which I assume you are since you're using Symfony then you can lifecycle events to listen for changes in your entities. Before saving you can then update your second column to the incremented value.
Regarding race conditions, to be sure you don't have bad data in your database you can put a unique constraint on your shop ID and invoice number.
I am looking to make PHP code that selects the best option in a data table. What is considered "best" would be based off of the variables/columns. I understand that I would need to start a mysqli query and create a couple of loops to search through the database, but I am not entirely sure how to implement something like this.
To give a more in-depth explanation of what I am talking about, here as an example.
(START EXAMPLE)
Lets say I have a database and there is a table with items in it. There are 3 columns: Item ID, Type, On Sale. I want to make it so that a user is able to pick out the best option based on those variables. In addition to finding the "best" option, it selects the one that is first listed in that order (in this case lowest Item ID).
Imagine this table:
Item ID | Type | On Sale
---------------------
1 | Chair | 0
2 | Table | 1
3 | Chair | 1
4 | Oven | 0
5 | Table | 1
6 | Oven | 0
The level of important goes like Type>On Sale>Item ID (lowest).
A user is looking for a chair. Item 3 is selected because it is his item and it is the first one also on sale.
A user is looking for a table. Item 2 is selected over Item 5 because it is listed higher (or in this case, has a lower Item ID)
A user is looking for an oven. Item 4 is selected because no ovens are on sale. Because no options are on sale, it selects the lowest Item ID of the ovens listed.
(END EXAMPLE)
So how should I go about this? Any answers would be greatly appreciated!
select * from table_name where Type = type_specified_by_user order by On Sale, Item ID
We have a database that has more than 70 tables with more than 10k of users. One of the tables in the database is to keep recording of user's latest actions/changes in order to keep them pending until the gameweek finishes (Football matches), let's call this table 'Substitute'.
However, every week when we advance the system (apply changes) at the end of gameweek, we check to see if there's any duplicated data stored regarding user's changes (we have noticed that a while ago), and remove them manually from the database.
The rows have primary keys (which is not duplicated) only the data is duplicated. It's like the insert query is being fired twice. (not sure about this or why)
Example: Let's say the user has a team of football players (11 on the field and 4 substitutes)
when the user chooses player and clicks substitute, that player (on the field) will be exchanged with the one who's in on the bench (Substitute). This process will happen immediately without the need of user to save (JavaScript). But, it not be saved in the team details, instead, it will be saved in the Substitute table that keep track of changes.
User wants to remove player 1 and enter player 12 (1->12). For the first step the system will record
id Team from_player to_player
0 x 1 12
And if the user do another substitution
id Team from_player to_player
0 x 1 12
1 x 2 13
And when the user substitute back the player (12->1) that record will be deleted(1->12), since the user substituted back the player and the later subs cancels the first.
id Team from_player to_player
1 x 2 13
But SOMETIMES it record that row more than once
id Team from_player to_player
0 x 1 12
1 x 1 12
2 x 1 12
3 x 2 13
Or the duplicate could be in between
id Team from_player to_player
0 x 1 2
1 x 2 13
2 x 1 2
3 x 1 2
N.B This happens only to around 10-100 users every week, even though there are more than 10k users registered in our database.
So what do you think is causing the problem?
I don't want to use INSERT IGNORE, I want to find the root of the problem and stop it.
So basically, my questions are:
1- Is it likely to be a server or client side problem?
2- Could ajax code be called/fired twice under certain circumstances?
3- Could be there an error in the sql server where it executes the same query twice?
Really appreciate your help.
UPDATE:
4- If the problem is with the client side, how i inspect it? Any suggested way?
For those who ask, what does happen when the user subs back the same player.
Let's say this is part of the original players stetting.
| Original Status | After First Subs|After Second Subs|
| | | |
On the field | p1 | P12 | P1 |
-----------------------|-----------------|-----------------|-----------------|
| | | |
Subs(at the bench) | p12 | P1 | P12 |
| | | |
Original Status p1 on the field, p12 on the bench (substitute)
Record of actions:
s1- P1->P12
Status= P12 on the field. P1 on the bench
s2- P12->P1
Status= P1 back on the field. P12 back to the bench.
Note that s2 wont be recorded, but, s1 will be deleted. it's like A * -1, and then -A * -1 again. The will cancel each other.
ajax mite be called more than once, tried inspecting using your firebug. you might need to disable your substitute method until the success flag is up.
Maybe the user fired the query more than once through the UI. Since the id is the PK, the firing the same query twice.
Another question.
When given the situation:
id Team from_player to_player
0 x 1 12
1 x 1 12
2 x 1 12
3 x 2 13
when user substitutes back 12->1, does your function delete "ALL" previous records or just 1?
I am developing a personal finance tracker (for fun!) and I have a table of categories. Each category is an entry in the table and at the end of the month they are all duplicated with their relevant balances reset to the start of the month reading for the new month.
Among others, these categories can be of type 'savings' and so have a running total. If I want to retrieve a category or update it then I used the category_id field and this works fine for the current working month but linking months together is breaking my brain. For the savings categories I want to show how the running_total has increased over the previous six months but in my current DB design, categories don't "know" about their previous months as they are created new at the start of each month.
The only way I could currently retrieve the last 6 months of a savings running_total is to search by the category name but this is potentially unreliable.
I have considered adding a field to the table which is "previous_month_category_id" which would work as a way to link the categories together but would be expensive to implement as it would require 6 MSQL operations each time grabbing the "previous_month_category_id" from the result and then re running the query.
If MYSQL can do some kind of recursion then maybe this could work but I feel like there is a more obvious answer staring me in the face.
I'm using Codeigniter and MYSQL but not scared of vanilla PHP if required.
Help on how to do this would be great.
UPDATE 1:
Below is a sample from what the savings category might look like mixed in amongst other categories. At the end of each month the entry is duplicated with the same category_name, type, buget, year, and users_id but the category_id auto increments, the month updates to the new month number and the running total is the previous running_total + the budget. How would I do one database query to retrieve these without using the category_name? As this could change is the user decided to caller it "Bigger TV" at the end of July
+-------------+--------------+------+--------+---------------+------+-------+----------+
| category_id |category_name | type | budget | running_total | year | month | users_id |
+-------------+--------------+------+--------+---------------+------+-------+----------+
| 44 | Big TV | sav | 20 | 240 | 2012 | 8 | 77 |
+-------------+--------------+------+--------+---------------+------+-------+----------+
| 32 | Big TV | sav | 20 | 220 | 2012 | 7 | 77 |
+-------------+--------------+------+--------+---------------+------+-------+----------+
| 24 | Big TV | sav | 20 | 200 | 2012 | 6 | 77 |
UPDATE 2:
I'm not sure I'm explaining myself very well So I'll put some more detail around how the app works and see if that helps.
I have tables called "categories", "transactions" and "users". A category can be one of three types, 1: Cash, 2: Regular Payment, 3: Savings. Think of cash and regular payment types as buckets, at the start of each month each bucket is full and the aim is to take money out of it and make sure there is still a bit left at the end of the month (or at least not negative).
This is fine on a month by month basis and works very well (for me, I have used this system for 2 years now I think). The trip up comes with Savings as they are linked month by month and are more like a big bucket that is added to each month (with a set increment called budget) until it overspills and is then drained (like Big TV would be when you buy it), or taken from a little bit here and there and the aim is to build up an emergency fund (like "When my car breaks down" type thing).
When the relevant information is displayed for each category only the current month is shown for cash and regular as that is all that is important, for the savings however the current amount is also shown but it would be nice to show a small history graph of how it had built up (or depleted) over time. To do this I need some way of searching for the previous end of month states of these categories so that the graph can be plotted but currently I can't work out how to link them all by anything other than the category_name.
I have tried to implement a bit of DB normalisation but this is the first schema I've implemented having known about normalisation so I've probably missed some aspects of it and possibly avoided any over normalisation where it didn't feel right.
Below are my tables:
categories
+-------------+--------------+------+--------+---------------+------+-------+----------+
| category_id |category_name | type | budget | running_total | year | month | users_id |
+-------------+--------------+------+--------+---------------+------+-------+----------+
transactions
+----------------+--------------+--------+------+----------+------------------------+
| transaction_id | description | amount | date | users_id | categories_category_id |
+----------------+--------------+--------+------+----------+------+-------+---------+
they are joined on categories_category_id which is a foreign key
I have always worked off the premise that each category needs an new entry for each month but it seems from the comments and answers below that I would be better off having just one category entry regardless of month and then just calculating everything on the fly?
Although, the budgets can be changed by the user and so for record keeping I'm not sure if this would work also the "deposits" never really happen it is just the category being duplicated at the end of the month so I guess that would need to dealt with.....
The aim of this app has always been to decouple financial tracking from the physical transaction that occur in a bank account and provide a layer over someones finances thus allowing the user to avoid hard to explain transactions etc and just focus on over all cash position. There is no concept of an "income" in this system, or a bank account.
It seems to me like your database design could use some work. I'm still not completely familiar with what you're really trying to do, but my initial thoughts would be to store each transaction as a single row in a table, and then query that table in different ways to generate different types of reports on it. Something like this:
transactions:
+----+---------+--------+---------------+-----------+-------------+
| id | user_id | amount | running_total | datestamp | category_id |
+----+---------+--------+---------------+-----------+-------------+
categories:
+----+------+------+
| id | name | type |
+----+------+------+
Don't increment the categories based on time. Add an entry to the categories table when you actually have a new category. If a transaction could possibly belong to multiple categories, then use a third (relational) table that relates transactions (based on transaction ID) to categories (based on category ID).
When you have a deposit, the amount field will be positive and for withdrawals, it will be negative. You can get your current running total by doing something like:
SELECT running_total FROM transactions
WHERE id = (SELECT MAX(id) FROM transactions WHERE user_id = '$userID');
You can find your total difference for a particular month by doing this:
SELECT SUM(amount) FROM transactions WHERE DATE('%c', datestamp) = '$monthNumber';
You can find the total spending for a particular category by doing this:
SELECT SUM(t.amount) FROM transactions t
INNER JOIN categories c ON t.category_id = c.id WHERE c.name = 'Big TV';
There are plenty of other possibilities, but the purpose here is just to demonstrate a possibly better way to store your data.