SUMmarize 3 columns from 2 different table in SQL - mysql

I have 3 database table. The one I store sell data like invoice number, due date, issue date. The second table I have the sell record id stored from previous table, there I store the ammount the person have payed (something like the customer payed only a half of the total price). In the third table I store the products and every row has the sell record id for backreference, in this table I have quantity and subtotal (quantity * price).
What I need is select all the record from first table and based on the sell_id I have to summarize how much they payed from the second table, summarize how many products one purchased and how much does it cost.
I need a little guidance to the right solution because I am stuck.
So far I came to this uggly sollution:
SELECT
(SELECT SUM(payment.sellpayment_amount) FROM es_sellpayment payment WHERE sell.sell_id = payment.sell_id) AS payed,
(SELECT SUM(product.sellproduct_quantity) FROM es_sellproduct product WHERE sell.sell_id = product.sell_id) AS quantity,
(SELECT SUM(product.sellproduct_total) FROM es_sellproduct product WHERE sell.sell_id = product.sell_id) AS total
FROM es_sell sell
It works I get the correct result, but I am unsure/unaware how this will impact later on the performance of the website. I tried the classic left join which gave me wrong results. I am also thinking storing permanently these 3 values on the main table; but this would be the lest preferable method because I had to make at lest 2 seperate SQL queries to retrieve the records.
CREATE TABLE `es_sell` (
`sell_id` int(11) NOT NULL,
`sell_invoice` int(11) NOT NULL,
`sell_note` text COLLATE utf8mb4_unicode_ci NOT NULL,
`sell_deliver` datetime NOT NULL,
`sell_issued` datetime NOT NULL,
`sell_due` datetime NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `es_sellproduct` (
`sellproduct_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`sellproduct_quantity` int(11) NOT NULL,
`sellproduct_price` int(11) NOT NULL,
`pricetype_id` int(11) NOT NULL,
`sellproduct_total` decimal(11,3) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `es_sellpayment` (
`sellpayment_id` int(11) NOT NULL,
`sellpayment_amount` decimal(11,3) NOT NULL,
`sell_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Use sub-queries to aggregate the associated tables to ensure that you're only ever joining one row to one row...
SELECT
sell.sell_id,
payment.payed,
product.quantity,
product.total
FROM
es_sell sell
LEFT JOIN
(
SELECT
sell_id,
SUM(sellpayment_amount) AS payed
FROM
es_sellpayment payment
GROUP BY
sell_id
)
payment
ON payment.sell_id = sell.sell_id
LEFT JOIN
(
SELECT
sell_id,
SUM(sellproduct_quantity) AS quantity,
SUM(sellproduct_total ) AS total
FROM
es_sellproduct
GROUP BY
sell_id
)
product
ON product.sell_id = sell.sell_id
If sell_id isn't unique in the sell table, you can aggregate again in the outer query.

Related

How to use WHERE IN in subquery with value from string column?

I have a problem to use WHERE IN with a column value
I have data like below
Table Service
id name sd_ids
1 House Cleaning 1,2
Table Service Detail
id name
1 living room
2 bedroom
Schema
CREATE TABLE IF NOT EXISTS `service` (
`id` int(6) unsigned NOT NULL,
`name` varchar(200) NOT NULL,
`sd_ids` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `service` (`id`, `name`,`sd_ids`) VALUES
('1','House Cleaning','1,2');
CREATE TABLE IF NOT EXISTS `service_detail` (
`id` int(6) unsigned NOT NULL,
`name` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `service_detail` (`id`, `name`) VALUES
('1','living room'),
('2','bedroom');
I already tried this
SELECT *,
(
SELECT
GROUP_CONCAT(name)
from service_detail
where id in(service.sd_ids)
) as service_detail
FROM service;
but the result it's not what I want
id name sd_ids service_detail
1 House Cleaning 1,2 living
I put schema and my testing here
http://sqlfiddle.com/#!9/49c34e/7
What I want to achieve to shows the result like below
id name sd_ids service_detail
1 House Cleaning 1,2 living room,bedroom
I know this schema is not best practice, but I need suggestion to achieve that.
thanks
You may join using FIND_IN_SET in the join criteria:
SELECT
s.id,
s.name,
s.sd_ids,
GROUP_CONCAT(sd.name ORDER BY sd.id) AS service_detail
FROM service s
INNER JOIN service_detail sd
ON FIND_IN_SET(sd.id, s.sd_ids) > 0
GROUP BY
s.id,
s.name,
s.sd_ids;
Demo
In general you should avoid storing CSV data in your SQL tables. CSV data represents unnormalized data, and is difficult to work with (q.v. the above query). Instead, break every service ID onto a separate record for best results.

SQL: How to store and select person who available on the same date range

From the image below, I want to...
1.Find solution to store unavailable date of each users.
2.I want to select users who available on specify date range.
Red = Unavailable,White = Available.
Example: I will have a training course on day 3 to 4, so i should get Mr.A and Mr.C as my query result.
Well, you can have a table like this:
Create table course_calendar (
date date not null,
course_id int not null,
tutor_id int default null,
primary key (date, course_id)
) engine = innodb;
create table tutor_calendar (
date date not null,
tutor_id int not null,
available enum('Y','N') default 'N',
Primary key (date, tutor_id)
) engine = innodb;
Then you would need to join the two tables and see which course_calendar has tutor_id is null
and see which tutor_calendar has tutor_id and available='Y' for the same dates.

SQL select entries in other table linked by foreign keys

I have redesigned my database structure to use PRIMARY and FOREIGN KEYs to link the entries in my 3 tables together, and I am having problems trying to write queries to select data in one table given data in a another table. Here is an example of my 3 CREATE TABLE statements:
CREATE TABLE IF NOT EXISTS players (
id INT(10) NOT NULL AUTO_INCREMENT,
username VARCHAR(16) NOT NULL,
uuid VARCHAR(200) NOT NULL DEFAULT 0,
joined TIMESTAMP DEFAULT 0,
last_seen TIMESTAMP DEFAULT 0,
PRIMARY KEY (id)
);
/* ^
One |
To
| One
v
*/
CREATE TABLE IF NOT EXISTS accounts (
id INT(10) NOT NULL AUTO_INCREMENT,
account_id INT(10) NOT NULL,
pass_hash VARCHAR(200) NOT NULL,
pass_salt VARCHAR(200) NOT NULL,
created BIGINT DEFAULT 0,
last_log_on BIGINT DEFAULT 0,
PRIMARY KEY (id),
FOREIGN KEY (account_id) REFERENCES players(id) ON DELETE CASCADE
) ENGINE=InnoDB;
/* ^
One |
To
| Many
v
*/
CREATE TABLE IF NOT EXISTS purchases (
id INT(10) NOT NULL AUTO_INCREMENT,
account_id INT(10) NOT NULL,
status VARCHAR(20) NOT NULL,
item INT NOT NULL,
price DOUBLE DEFAULT 0,
description VARCHAR(200) NOT NULL,
buyer_name VARCHAR(200) NOT NULL,
buyer_email VARCHAR(200) NOT NULL,
transaction_id VARCHAR(200) NOT NULL,
payment_type VARCHAR(20) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id) ON DELETE CASCADE
) ENGINE=InnoDB;
Say for example, I want to select all the usernames of users who purchased anything greater than $30. All the usernames are stored in the players table, which is linked to the accounts table and that is linked to the purchases table. Is this this the best way to design this relational database? If so, how would I run queries similar to the above example?
I was able to get get all of a users purchase history given their username, but I did it with 2 sub-queries... Getting that data should be easier than that!
Here is the SELECT query I ran to get all of a players purchase data:
SELECT *
FROM purchases
WHERE account_id = (SELECT id FROM accounts WHERE account_id = (SELECT id FROM players WHERE username = 'username'));
Also, when I try to make references to the other tables using something like 'players.username', I get an error saying that the column doesn't exist...
I appreciate any help! Thanks!
Your design is ok in my opinion. The relation between players and account is one-to-many and not one-to-one since this way, you can have two tuples referencing a single player.
I would write the query you need as:
SELECT DISTINCT p.id, p.username
FROM players p INNER JOIN accounts a ON (p.id = a.account_id)
INNER JOIN purchases pc ON (a.id = pc.account_id)
WHERE (pc.price > 30);
As Sam suggested, I added DISTINCT to avoid repeating id and username in case a user have multiple purchases.
Note the id is here to avoid confusion among repeated usernames.

Sorting a SQL query by number of fields in a grouped selection

I have this table,
CREATE TABLE `Products` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`product` varchar(45) DEFAULT NULL,
`added_by` varchar(45) DEFAULT NULL,
`list_name` varchar(45) NOT NULL,
PRIMARY KEY (`product_id`)
) ENGINE=MyISAM AUTO_INCREMENT=231 DEFAULT CHARSET=utf8
and i want an sql query such that i get groups of same-named products ordered by which group has the most entries at the top and fewest at the bottom
This is what i have so far, but it only sorts the groups alphabetically
SELECT product FROM Products GROUP BY product ORDER BY product
what do i add to the end of this to count and sort by the number of entries in each group?
SELECT product, COUNT(*) AS NumberOfEntries
FROM Products
GROUP BY product
ORDER BY NumberOfEntries DESC
Read more about aggregate functions here.
You could try something like this:
SELECT product
FROM
(
SELECT product, COUNT( 1 ) AS num_products
FROM products
GROUP BY product
) AS t
ORDER BY num_products DESC

How to calculate the due amount of this payments?

I want to create a query to calculate the due amount, on a situation where partial payments can be done for specific paymentSchedule id
I have a table with a payment schedule as this:
CREATE TABLE IF NOT EXISTS paymentSchedule (
id int(11) NOT NULL AUTO_INCREMENT,
concept varchar(32) COLLATE utf8_spanish_ci NOT NULL,
student_id int(11) NOT NULL,
dueDate date NOT NULL,
amount decimal(10,2) NOT NULL,
PRIMARY KEY (id)
)
And I have a payments transaction table like this, wherepaymentSchedule_id refers to the above table:
CREATE TABLE IF NOT EXISTS ledger (
id int(11) NOT NULL AUTO_INCREMENT,
paymentDate date NOT NULL,
amount decimal(10,2) NOT NULL,
paymentSchedule_id int(11) NOT NULL,
PRIMARY KEY (id)
)
I tried to to a substraction like here:
How to substract from the resut of two selects in mysql
but that only works if there are fields with the correspondinf paymentSchedule id in the ledger.
What is the right way to do it?
on the other hand, is that the right way to store payment schedules and transactions?
It seems like you are trying to do this?
for each id in paymentSchedule table:
store paymentSchedule.amount - sum( ledger.amount )
In which case you could do:
SELECT paymentSchedule.id,
paymentSchedule.amount - IFNULL(SUM(ledger.amount),0) AS remaining
FROM paymentSchedule
LEFT JOIN ledger ON paymentSchedule.id=ledger.paymentSchedule_id
GROUP BY ledger.paymentSchedule_id
This shows all amounts remaining, regardless of whether a person has paid any of it off or not (thanks to the IFNULL( xxx, 0 )).