MySQL Advanced SUM function - mysql

Good day!
I am sorry If this question was already asked, but I am kind of in hurry, so I am not sure.
I have 2 tables:
Table.Cars and Table.Price
In table Cars:
ID Auto_Increment Primary key NOT NULL
Name VARCHAR(30)
In table Price:
CarsID Primary key NOT NULL
Units Int
Price_per_unit decimal(10,2)
Total decimal(10,2)
So, what I need to do is, Create column (TOTAL* (Name does not need to be necessary this one)), in table Price, Set it to do Mathematical function.
Basicly, I want Column.Total to show Units * price_per_unit which would be:
ALTER TABLE Price
ADD TOTAL Decimal(10,2)
UPDATE Price SET Total = SUM(Units * Price_per_unit);
Which will look like this:
---------------------------------------------------
| ID | Units | Price_per_unit | Total | TOTAL |
---------------------------------------------------
| 1 | 12 | 30000,00 | 360000,00 | NULL |
| 2 | 5 | 12000,00 | 60000,00 | NULL |
And here comes my problem. I want to make TOTAL, to look like this:
---------------------------------------------------------
| ID | Units | Price_per_unit | Total | TOTAL |
---------------------------------------------------------
| 1 | 12 | 30000,00 | 360000,00 | 420000 |
| 2 | 5 | 12000,00 | 60000,00 | |
|_______________________________________________________|
So, next time, I put new record in table Price, I need TOTAL, to auto SUM my table by taking data from Units * Price_pre_unit.
So my question is, how do I make this query?

If you want Mysql database to do it automatically, it seems you are looking for a trigger. You should tun this code one time and the trigger will handle it for ever:
CREATE TRIGGER trigger_name
AFTER INSERT
ON price FOR EACH ROW
BEGIN
UPDATE Price SET Total = SUM(Units * Price_per_unit) where id=(select id from price order by id limit 1);
END;

Related

Suppose we have two tables How do we take a number `X` from table 1, look-up `X` in table 2 to get Y, Then enter `Y` into table 1

This question is about a very basic SQL operation.
We have two tables:
+-----------------------+
| game_results |
+--------+-------+------+
| Name | Score | Rank |
+--------+-------+------+
| David | 100 | null |
| Jacob | 100 | null |
| Gordon | 99.9 | null |
| Ian | 99.9 | null |
| Sarah | 99.9 | null |
| Robert | 1000 | null |
+--------+-------+------+
+-------+-------+
| ranked_scores |
+-------+-------+
| Score | Rank |
+-------+-------+
| 1000 | 1 |
| 100 | 2 |
| 99.9 | 3 |
+-------+-------+
Our goal is to write some SQL which will fill-in The Rank row in the GAME RESULTS table.
How do we write SQL to accomplish the following:
choose a particular score from the GAME RESULTS table (such as 100)
look-up that score in the ranked_scores table to get a rank (such as 2)
then enter the rank into the GAME RESULTS table
EXAMPLE:
Note that Gordon has score 99.9
Look-up 99.9 in the ranked_scores table to see that the rank number is 3.
Record that Gordon has rank 3 in the game_results table.
I wrote the following pseudo-code, but it has for-loops, and does not look at all like SQL:
for each row `r1` in the table named `game_results` {
Let `score` be the score value of row `r`
// Example:
// If `r1` is the following row
// +--------+-------+------+
// | David | 100 | null |
// +--------+-------+------+
//
// then `score` is:
// 100
Let `reduce_ranked_score` be a new table created from `ranked_scores`
such that `reduce_ranked_score` only contains the rows where the score
value is equal to `score`
For every row `r2` in `reduce_ranked_score`, (r2.Score == score)
// Example:
// if `r1` is the following row
// | David | 100 | null |
//
// then `reduce_ranked_score` is:
//
// +-------+-------+
// | Score | Rank |
// +-------+-------+
// | 100 | 2 |
// +-------+-------+
Let `rank` be the rank value of the only row in the table named `reduce_ranked_score`
overwrite `r1.rank` with a copy the value of `rank`
}
The following is some SQL I wrote to generate the ranked_scores table:
CREATE VIEW distinct_scores AS
SELECT DISTINCT Score
FROM table_name;
CREATE VIEW ranked_scores AS
SELECT
ROW_NUMBER() OVER (
ORDER BY Score
) Rank,
Score,
Rank
FROM
distinct_scores;
You would use update with a join:
update game_results gr join
ranked_scores rs
on gr.score = rs.score
set gr.rank = rs.rank
where rs.score = 100; -- leave this out if you want to update all scores
That said, it is often better to leave the data in two separate tables and to join the tables when you need the information. Duplicating the same information across multiple tables is considered a bad design practice -- sometimes appropriate, but you should really know what you are doing and why.

How to preserve value of one column based on other column values?

I have a requirement wherein I will be getting records which I need to insert into a database (MariaDB 10.3) table wherein for each record I have 2 base values viz. name and amount and one processed value viz. action (imagine this action is something acted upon by user from UI).
+---------+--------+----------------+----------------+
| name | amount | action | created_at |
+---------+--------+----------------+----------------+
| Akshay | 1000 | processed | 2019-08-01 |
+---------+--------+----------------+----------------+
Now, what I want to achieve is when next time I receive a record with name and amount which already exist in the table, then populate action by automatic reference to the previous entry from this same table whose name and amount match.
And if name and amount combination does not exist in the table, then do not populate action.
Desired end result is depicted in structure below:
+---------+--------+----------------+----------------+
| name | amount | action | created_at |
+---------+--------+----------------+----------------+
| Akshay | 1000 | processed | 2019-08-04 |
| Akshay | 1001 | | 2019-08-03 |
| Saanvi | 1000 | | 2019-08-02 |
| Akshay | 1000 | processed | 2019-08-01 |
+---------+--------+----------------+----------------+
Any clues how can I achieve this functionality?
DROP PROCEDURE IF EXISTS db.SP_CREATE_VALUE;
CREATE PROCEDURE db.`SP_CREATE_VALUE`(IN `in_name` VARCHAR(50), IN `in_amount` INT)
BEGIN
DECLARE numAlreadyExists INT(11) DEFAULT 0;
DECLARE strExistingAction VARCHAR(20) DEFAULT "";
SET numAlreadyExists=(SELECT COUNT(*) FROM table_name WHERE name=in_name AND in_amount=in_amount);
IF (numAlreadyExists >0) THEN
SET strExistingAction=(SELECT action FROM table_name WHERE name=in_name AND in_amount=in_amount ORDER BY table_id DESC LIMIT 1);
END IF;
INSERT INTO table_name
name=in_name,amount=in_amount,action=strExistingAction;
END;
And then when you want to create a new record, simply...
CALL SP_CREATE_VALUE('Akshay',1000);
You can use window functions. For instance:
select name, amount,
max(action) over (partition by name, amount order by date) as action,
date
from t;

How to occupy something (in this case a wardrobe slot) in MySQL

I'm developing a wardrobe application that uses a database table called "entrances".
The program is used to organize a normal wardrobe storage where the storage can have different amount of numbers/slots to hang clothes on. When a customer comes up to the merchant, the merchant scans the customer's bar code and will then get a free number from the system to hang the customer's clothes on. But there can of course only be one entry for each number.
My entrances db could look something like:
ID | wardrobeNo | storeID | customerBarcode | deliveredTime | collectedTime
---+------------+---------+-----------------+---------------+--------------
1 | 1 | 1 | XX | 20:12:55 | NULL
2 | 2 | 1 | XA | 20:44:44 | NULL
3 | 1 | 2 | XZ | 20:55:55 | NULL
4 | 2 | 2 | XC | 22:22:22 | NULL
Later that day the same entries do still exist in the DB but they will now have a collected time if the clothes have been collected from the wardrobe on some of the numbers before people went home.
ID | wardrobeNo | storeID | customerBarcode | deliveredTime | collectedTime
---+------------+---------+-----------------+---------------+--------------
1 | 1 | 1 | XX | 20:12:55 | 23:23:23
2 | 2 | 1 | XA | 20:44:44 | NULL
3 | 1 | 2 | XZ | 20:55:55 | 22:23:23
4 | 2 | 2 | XC | 22:22:22 | NULL
I will then be able to see the occupied numbers with:
SELECT * FROM db WHERE storeID = x AND delivered NOT NULL AND collected = NULL
What i'm wondering about is how I would be able to lock these 'wardrobeNo' while the merchant is handling payment, so another merchant does not make order on the same 'wardrobeNo'... just like a restaurant that would link orders to tables.
Is this even a good way to tackle the problem or is there something a lot smarter? Or should I consider thinking about this problem in another way.
Hope it makes sense..
Updated: Instead of taking care of maintaining a sequence yourself, use MySQL's auto_increment in combination with a scheduled alter table command at midnight:
CREATE TABLE idTable (
idKey INT(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (idKey)
)
And at midnight:
TRUNCATE TABLE idTable;
ALTER TABLE idTable AUTO_INCREMENT = 1;
Then simply add a new record to idTable prior to adding a row to your wardrobe table and use the inserted ID (via mysql_insert_id()) to get a daily unique ID.

MySQL second auto increment field based on foreign key

I've come across this problem numerous times but haven't found a "MySQL way" to solve the issue as such - I have a database that contains users and reports. Each report has an id which I display as a report number to my users.
The main complaint is that users are confused as to why reports have gone missing from their system. This is not actually the case. It is actually that they are recognizing a gap between their IDs and assume that these are missing reports, when in actual fact, it is simply becasue another user has filled in this auto-incrementing gap.
I need to know if there is a way to do this in MySQL:
Is it possible that I can have a second auto-increment field called report_number which is based on a user_id field which has a different set of auto-increments per user?
e.g.
|------|---------|---------------|
| id | user_id | report_number |
|------|---------|---------------|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 1 |
| 5 | 1 | 4 |
| 6 | 1 | 5 |
| 7 | 2 | 2 |
| 8 | 3 | 1 |
| 9 | 3 | 2 |
|------|---------|---------------|
I am using InnoDB for this as it is quite heavily weighted with foreign-keys. It appears to complain when I add a second auto increment field, but I wasn't sure if there was a different way to do this?
MyISAM supports the second column with auto increment, but InnoDB doesn't.
For InnoDB you might create a trigger BEFORE INSERT to get the max value of the reportid and add one to the value.
DELIMITER $$
CREATE TRIGGER report_trigger
BEFORE INSERT ON reports
FOR EACH ROW BEGIN
SET NEW.`report_id` = (SELECT MAX(report_id) + 1 FROM reports WHERE user_id = NEW.user_id);
END $$
DELIMITER ;
If you can use MyISAM instead, in the documentation of MySQL page there is an example:
http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html
CREATE TABLE animals (
grp ENUM('fish','mammal','bird') NOT NULL,
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (grp,id)
) ENGINE=MyISAM;
INSERT INTO animals (grp,name) VALUES
('mammal','dog'),('mammal','cat'),
('bird','penguin'),('fish','lax'),('mammal','whale'),
('bird','ostrich');
SELECT * FROM animals ORDER BY grp,id;
Which returns:
+--------+----+---------+
| grp | id | name |
+--------+----+---------+
| fish | 1 | lax |
| mammal | 1 | dog |
| mammal | 2 | cat |
| mammal | 3 | whale |
| bird | 1 | penguin |
| bird | 2 | ostrich |
+--------+----+---------+
Right one with IFNULL:
DELIMITER $$
CREATE TRIGGER salons_trigger
BEFORE INSERT ON salon
FOR EACH ROW BEGIN
SET NEW.salon_id = IFNULL((SELECT MAX(salon_id) + 1 FROM salon WHERE owner = NEW.owner), 1);
END $$
DELIMITER ;
I think mysql doesnt support two auto_increment columns. you can create report number using information schema.
select NULL from information_schema.columns
MySQl does not support two auto incremented fields, if you need then create another table, set the other field which you want to be as auto incremented and you must set up a relationship with these two tables.

SQL algorithm to as near to linear time as possible and tweaking of select statement

I am using MySQL version 5.5 on Ubuntu.
My database tables are setup as follows:
DDLs:
CREATE TABLE 'asx' (
'code' char(3) NOT NULL,
'high' decimal(9,3),
'low' decimal(9,3),
'close' decimal(9,3),
'histID' int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY ('histID'),
UNIQUE KEY 'code' ('code')
)
CREATE TABLE 'asxhist' (
'date' date NOT NULL,
'average' decimal(9,3),
'histID' int(11) NOT NULL,
PRIMARY KEY ('date','histID'),
KEY 'histID' ('histID'),
CONSTRAINT 'asxhist_ibfk_1' FOREIGN KEY ('histID') REFERENCES 'asx' ('histID')
ON UPDATE CASCADE
)
t1:
| code | high | low | close | histID (primary key)|
| asx | 10.000 | 9.500 | 9.800 | 1
| nab | 42.000 | 41.250 | 41.350 | 2
t2:
| date | average | histID (foreign key) |
| 2013-01-01| 10.000 | 1 |
| 2013-01-01| 39.000 | 2 |
| 2013-01-02| 9.000 | 1 |
| 2013-01-02| 38.000 | 2 |
| 2013-01-03| 9.500 | 1 |
| 2013-01-03| 39.500 | 2 |
| 2013-01-04| 11.000 | 1 |
| 2013-01-04| 38.500 | 2 |
I am attempting to complete a select query that produces this as a result:
| code | high | low | close | asxhist.average |
| asx | 10.000 | 9.500 | 9.800 | 11.000, 9.5000 |
| nab | 42.000 | 41.250 | 41.350 | 38.500,39.500 |
Where the most recent information in table 2 is returned with table 1 in a csv format.
I have managed to get this far:
SELECT code, high, low, close,
(SELECT GROUP_CONCAT(DISTINCT t2.average ORDER BY date DESC SEPARATOR ',') FROM t2
WHERE t2.histID = t1.histID)
FROM t1;
Unfortunately this returns all values associated with hID. I'm taking a look at xaprb.com's firstleastmax-row-per-group-in-sql solution but I have been banging my head all day and the slight wooziness seems to be dimming my ability to comprehend how I should use it to my benefit. How can I limit the results to the most 5 recent values and considering the tables will eventually be megabytes in size, try and remain in O(n2) or less? (Or can I?)
Temporary work around using SUBSTRING_INDEX and not a feasible solution for huge data
SELECT code, high, low, close,
(SELECT SUBSTRING_INDEX(GROUP_CONCAT(asxhist.average), ',', 3)
FROM asxhist
WHERE asxhist.histID = asx.histID
ORDER BY date DESC)
FROM asx;
From what I gather Limit option in GROUP_CONCAT is still under feature-request.
Also on stackoverflow hack MySQL GROUP_CONCAT