Mysql: How to join a query to find results from another table - mysql

I have two tables:
TABLE A
Unique_id
id
price
1
1
10.50
2
3
14.70
3
1
12.44
TABLE B
Unique_id
Date
Category
Store
Cost
1
2022/03/12
Shoes
A
13.24
2
2022/04/15
Hats
A
15.24
3
2021/11/03
Shoes
B
22.31
4
2000/12/14
Shoes
A
15.33
I need to filter TABLE A on a known id to get the Unique_id and average price to join to Table B.
Using this information I need to know which stores this item was sold in.
I then need to create a results table displaying the stores and the amount of days sales were recorded in the stores - regardless of whether the sales are associated with the id and the average cost.
To put it more simply I can break down the task into 2 separate commands:
SELECT AVG(price)
FROM table_a
WHERE id = 1
GROUP BY unique_id;
SELECT store, COUNT(date), AVG(cost)
FROM table_b
WHERE category = 'Shoes'
GROUP BY store;
The unique_id should inform the join but when I join the tables it messes up my COUNT function and only counts the days in which the id is connected - not the total store sales days.
The results should look something like this:
Store
AVG price
COUNT days
AVG cost
A
10.50.
3
14.60.
B
12.44
1.
22.31.

I wwas hard to grasp, what you wanted, but after some thinking and your clarification, it can be solved as the code shows
CREATE TABLE TableA
(`Unique_id` int, `id` int, `price` DECIMAL(10,2))
;
INSERT INTO TableA
(`Unique_id`, `id`, `price`)
VALUES
(1, 1, 10.50),
(2, 3, 14.70),
(3, 1, 12.44)
;
CREATE TABLE TableB
(`Unique_id` int, `Date` datetime, `Category` varchar(5), `Store` varchar(1), `Cost` DECIMAL(10,2))
;
INSERT INTO TableB
(`Unique_id`, `Date`, `Category`, `Store`, `Cost`)
VALUES
(1, '2022-03-12 01:00:00', 'Shoes', 'A', 13.24),
(2, '2022-04-15 02:00:00', 'Hats', 'A', 15.24),
(3, '2021-11-03 01:00:00', 'Shoes', 'B', 22.31),
(4, '2000-12-14 01:00:00', 'Shoes', 'A', 15.33)
SELECT
B.`Store`
, AVG(A.`price`) price
, (SELECT COUNT(*) FROM TableB WHERE `Store` = B.`Store` ) count_
, (SELECT AVG(
`cost`) FROM TableB WHERE `Store` = B.`Store` ) price
FROM TableA A
JOIN TableB B ON A.`Unique_id` = B.`Unique_id`
WHERE B.`Category` = 'Shoes'
GROUP BY B.`Store`
Store | price | count_ | price
:---- | --------: | -----: | --------:
A | 10.500000 | 3 | 14.603333
B | 12.440000 | 1 | 22.310000
db<>fiddle here

This should be the query you are after. Mainly you simply join the rows using an outer join, because not every table_b row has a match in table_a.
Then, the only hindrance is that you only want to consider shoes in your average price. For this to happen you use conditional aggregation (a CASE expression inside the aggregation function).
select
b.store,
avg(case when b.category = 'Shoes' then a.price end) as avg_shoe_price,
count(b.unique_id) as count_b_rows,
avg(b.cost) as avg_cost
from table_b b
left outer join table_a a on a.unique_id = b.unique_id
group by b.store
order by b.store;
I must admit, it took me ages to understand what you want and where these numbers result from. The main reason for this is that you have WHERE table_a.id = 1 in your query, but this must not be applied to get the result you are showing. Next time please look to it that your description, queries and sample data match.
(And then, I think that names like table_a, table_b and unique_id don't help understanding this. If table_a were called prices instead and table_b costs and unique_id were called cost_id then, I wouldn't have had to wonder how the tables are related (by id? by unique id?) and wouldn't have had to look again and again which table the cost resides in, which table has a price and which table is the outer joined one while looking at the problem, the requested result and while writing my query.)

Related

MySQL select row from one table with multiple rows in a second table and get array of multi row in selected row

i have one table containing "Client" information, and another including "Tickets" information for each client.
int-------| varchar -------| varchar
client_id | client_name | client_tickets
----------+----------------+--------------
1 | Title one | 1,2
2 | Title two | 2,3
Simplified tickets table
int--------| varchar -------| varchar
ticket_id | ticket_name | ticket_price
-----------+-------------+--------------
1 | ticketone | 30
2 | tickettwo | 40
3 | ticketthree | 50
4 | ticketfour | 60
5 | ticketfive | 70
With the above two tables, I want to produce a single table with a single query with all the pertinent information to generate a search grid
So as to give the following output :
client_id | client_name | client_tickets | ticket_names | ticket_prices
----------+----------------+----------------+-----------------------+--
1 | Title one | 1,2 | ticketone,tickettwo | 30,40
2 | Title two | 2,3 | tickettwo,ticketthree | 40,50
ticket_names,ticket_ids,client_name are varchar
I want to receive the final 5 columns with one request
for example :
SELECT s.*,
(SELECT GROUP_CONCAT(ticket_name SEPARATOR ',') FROM tickets_table WHERE ticket_id IN(s.client_tickets)) AS ticket_names,
(SELECT GROUP_CONCAT(ticket_price SEPARATOR ',') FROM tickets_table WHERE ticket_id IN(s.client_tickets)) AS ticket_prices
FROM client_table s where s.client_id=1
Which seems to have a problem
Do you have a better suggestion?
Please make your suggestions
Update :
To clean the result I want
The following code has two querys,
I want this code to be done with a query
$client_result = $conn->query("SELECT * FROM client_table where client_id=1");
while($client_row = $client_result->fetch_assoc()) {
$ticket_result = $conn->query("SELECT * FROM tickets_table where ticket_id IN ($client_row['client_tickets'])");
while($ticket_row = ticket_result->fetch_assoc()) {
echo $ticket_row['ticket_name']."<br>";
}
}
update 2
i use suggest #raxi , but my mariadb is 10.4.17-MariaDB and don't support JSON_ARRAYAGG , for resolve it according to the reference Creating an aggregate function
, Using SQL
DELIMITER //
DROP FUNCTION IF EXISTS JSON_ARRAYAGG//
CREATE AGGREGATE FUNCTION IF NOT EXISTS JSON_ARRAYAGG(next_value TEXT) RETURNS TEXT
BEGIN
DECLARE json TEXT DEFAULT '[""]';
DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN json_remove(json, '$[0]');
LOOP
FETCH GROUP NEXT ROW;
SET json = json_array_append(json, '$', next_value);
END LOOP;
END //
DELIMITER ;
What you want a fairly straightforward SELECT query with some LEFT/INNER JOIN(s).
This website has some good examples/explanations which seem very close to your need: https://www.mysqltutorial.org/mysql-inner-join.aspx
I would give you a quick working example, but it is not really clear to me what datatype the relevant columns are. Both tables' _id-columns are likely some variant of INTEGER, are they also both primary keys (or otherwise atleast indexed ?), the client_name/ticket_name are likely VARCHAR/TEXT/STRING types, but how exactly is the remaining column stored? as json or array or ? (+details)
Also you tagged your post with PHP, are you just after the SQL query ? or looking for PHP code with the SQL inside it.
updated
Improved version of the schema
CREATE TABLE clients (
client_id SERIAL,
client_name VARCHAR(255) NOT NULL,
PRIMARY KEY (client_id)
);
CREATE TABLE tickets (
ticket_id SERIAL,
ticket_name VARCHAR(255) NOT NULL,
ticket_price DECIMAL(10,2) NOT NULL,
PRIMARY KEY (ticket_id)
);
-- A junction table to glue those 2 tables together (N to N relationship)
CREATE TABLE client_tickets (
client_id BIGINT UNSIGNED NOT NULL,
ticket_id BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (client_id, ticket_id)
);
I have changed the datatypes.
client_name and ticket_name are still VARCHARS. I've flagged them as NOT NULL (eg: required fields), but you can remove that part if you don't like that.
client_id/ticket_id/ticket_price are also NOT NULL but changing that has negative side-effects.
ticket_price is now a DECIMAL field, which can store numbers such as 1299.50 or 50.00 The (10,2) bit means it covers every possible number up to 8 whole digits (dollars/euros/whatever), and 2 decimals (cents). so you can store anything from $ -99.999.999,99 to $ 99.999.999,99 .
in SQL always write numbers (like lets say 70k) in this notation: 70000.00 (eg: a dot, not a comma; and no thousandseperators).
client_id and ticket_id are both SERIALs now, which is shorthand for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE and theyre both PRIMARY KEYs on top of that. That probably sounds complicated but they're still just ordinary INTEGERs with values like 4 or 12 etc.
The UNIQUE bit prevents you from having 2 clients with the same ID number, and the AUTO_INCREMENT means that when you add a new client, you dont have to specify an ID (though you are allowed to); you can just do:
INSERT INTO clients (client_name) values ('Fantastic Mr Fox');
and the client_id will automatically be set (incrementing over time). And the same goes for ticket_id in the other table.
.
I've replaced your original client_tickets column, into a separate junction table.
Records in there store the client_id of a client and the ticket_id that belongs to them.
A client can have multiple records in the junction table (one record for each ticket they own).
Likewise, a ticket can be mentioned on any number of rows.
It's possible for a certain client_id to not have any records in the junction table.
Likewise, it's possible for a certain ticket_id to not have any records in the junction table.
Identical records cannot exist in this table (enforced by PRIMARY KEY).
Testdata
Next, we can put some data in there to be able to test it:
-- Create some tickets
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (1, 'ticketone', '30' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (2, 'tickettwo', '40' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (3, 'ticketthree', '50' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (4, 'ticketfour', '60' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (5, 'ticketfive', '70' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (6, 'ticketsix', '4' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (7, 'ticketseven', '9' );
INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (8, 'ticketeight', '500' );
-- Create some users, and link them to some of these tickets
INSERT INTO clients (client_id, client_name) values (1, 'John');
INSERT INTO client_tickets (client_id, ticket_id) values (1, 3);
INSERT INTO client_tickets (client_id, ticket_id) values (1, 7);
INSERT INTO client_tickets (client_id, ticket_id) values (1, 1);
INSERT INTO clients (client_id, client_name) values (2, 'Peter');
INSERT INTO client_tickets (client_id, ticket_id) values (2, 5);
INSERT INTO client_tickets (client_id, ticket_id) values (2, 2);
INSERT INTO client_tickets (client_id, ticket_id) values (2, 3);
INSERT INTO clients (client_id, client_name) values (3, 'Eddie');
INSERT INTO client_tickets (client_id, ticket_id) values (3, 8);
INSERT INTO clients (client_id, client_name) values (9, 'Fred');
-- Note: ticket #3 is owned by both client #1/#2;
-- Note: ticket #4 and #6 are unused;
-- Note: client #9 (Fred) has no tickets;
Queries
Get all the existing relationships (ticket-less clients are left out & owner-less tickets are left out)
SELECT clients.*
, tickets.*
FROM client_tickets AS ct
INNER JOIN clients ON ct.client_id = clients.client_id
INNER JOIN tickets ON ct.ticket_id = tickets.ticket_id
ORDER BY clients.client_id ASC
, tickets.ticket_id ASC ;
Get all the tickets that are still free (owner-less)
SELECT tickets.*
FROM tickets
WHERE tickets.ticket_id NOT IN (
SELECT ct.ticket_id
FROM client_tickets AS ct
)
ORDER BY tickets.ticket_id ASC ;
Get a list of ALL clients (even ticketless ones), and include how many tickets each has and the total price of their tickets.
SELECT clients.*
, COALESCE(COUNT(tickets.ticket_id), 0) AS amount_of_tickets
, COALESCE(SUM(tickets.ticket_price), 0.00) AS total_price
FROM clients
LEFT JOIN client_tickets AS ct ON ct.client_id = clients.client_id
LEFT JOIN tickets ON ct.ticket_id = tickets.ticket_id
GROUP BY clients.client_id
ORDER BY clients.client_id ASC ;
Put all the juicy info together (owner-less tickets are left out)
SELECT clients.*
, COALESCE(COUNT(sub.ticket_id), 0) AS amount_of_tickets
, COALESCE(SUM(sub.ticket_price), 0.00) AS total_price
, JSON_ARRAYAGG(sub.js_tickets_row) AS js_tickets_rows
FROM clients
LEFT JOIN client_tickets AS ct ON ct.client_id = clients.client_id
LEFT JOIN (
SELECT tickets.*
, JSON_OBJECT( 'ticket_id', tickets.ticket_id
, 'ticket_name', tickets.ticket_name
, 'ticket_price', tickets.ticket_price
) AS js_tickets_row
FROM tickets
) AS sub ON ct.ticket_id = sub.ticket_id
GROUP BY clients.client_id
ORDER BY clients.client_id ASC ;
-- sidenote: output column `js_tickets_rows` (a json array) may contain NULL values
An list of all tickets with some aggregate data
SELECT tickets.*
, IF(COALESCE(COUNT(clients.client_id), 0) > 0
, TRUE, FALSE) AS active
, COALESCE( COUNT(clients.client_id), 0) AS amount_of_clients
, IF(COALESCE( COUNT(clients.client_id), 0) > 0
, GROUP_CONCAT(clients.client_name SEPARATOR ', ')
, NULL) AS client_names
FROM tickets
LEFT JOIN client_tickets AS ct ON ct.ticket_id = tickets.ticket_id
LEFT JOIN clients ON ct.client_id = clients.client_id
GROUP BY tickets.ticket_id
ORDER BY tickets.ticket_id ASC
, clients.client_id ASC ;

Join Two tables but replace overlapping data with second tables

Hello I have an issue I am working on for a theoretical problem. Assume I have these two tables
Order Table
Entry
Order#
DatePlaced
Type
2001
5
2021-05-03
C
Status Table
Entry
Order#
Status
Date
Deleted
2001
5
S
2021-05-04
0
2002
5
D
2021-05-05
0
So I need to be able to get this
Expected Table
Entry
Order#
DatePlaced
Type
Status
Date
Deleted
2002
5
2021-05-03
C
D
2021-05-05
0
This would be fairly easy if I could just left join the data. The is issue is that the sql in the code is already written like this. The tables are joined based on the entry. Every time a new status occurs for an order# the entry in the Order Table is updated EXCEPT when it is delivered. Do to how dependent the code is I cannot simply update the initial query below. I was wondering if there is a join or way without using SET that I can get the last status based on the order? I was thinking we can check the order and then the entry but I am not sure how to join that with the Current Table (data we get from query)
SELECT * FROM orders or
LEFT JOIN status st ON or.entry = st.entry
WHERE st.deleted = 0;
This results in this
Current Table
Entry
Order#
DatePlaced
Type
Status
Date
Deleted
2001
5
2021-05-03
C
S
2021-05-04
0
Is there a way to JOIN the status table with the Current Table so that the status columns become what I expect?
This will work just fine:
SELECT s.entry, s.order_no, o.date_placed, o.type, s.status, s.date, s.deleted
FROM `orders` o
INNER JOIN `status` s ON (
s.order_no=o.order_no AND s.entry=(SELECT MAX(entry) FROM status WHERE order_no = o.order_no)
)
Live Demo
https://www.db-fiddle.com/f/twz1TT9VH7YNTY1KrpRAjx/3
Does the last status have a higher entry number or higher date created?
Perhaps include MAX(st.Entry) as last_entry in your SELECT clause,
Maybe select your fields explicitly
vs SELECT *
and include a
GROUP BY
after your WHERE clause
and a
HAVING
after your GROUP BY
create table orders (
entry INT,
order_number INT,
date_placed date,
order_type VARCHAR(1) )
create table order_status (
entry INT,
order_number INT,
order_status VARCHAR(1),
date_created date,
deleted INT
);
INSERT INTO orders (entry, order_number, date_placed, order_type) VALUES (2001, 5, '2021-05-03', 'C');
INSERT INTO order_status (entry, order_number, order_status, date_created, deleted)
VALUES
(2001, 5, 'S', '2001-05-04', 0),
(2002, 5, 'D', '2001-05-05', 0);
SELECT os.entry, o.order_number, o.date_placed, o.order_type,
os.order_status, os.date_created, os.deleted,
MAX(os.entry) as last_entry
FROM orders o
LEFT JOIN order_status os
ON o.order_number = os.order_number
GROUP BY o.order_number
HAVING os.entry = last_entry

Selecting all rows with only one value in column with another common value

my table:
drop table if exists new_table;
create table if not exists new_table(
obj_type int(4),
user_id varchar(30),
payer_id varchar(30)
);
insert into new_table (obj_type, user_id, payer_id) values
(1, 'user1', 'payer1'),
(1, 'user2', 'payer1'),
(2, 'user3', 'payer1'),
(1, 'user1', 'payer2'),
(1, 'user2', 'payer2'),
(2, 'user3', 'payer2'),
(3, 'user1', 'payer3'),
(3, 'user2', 'payer3');
I am trying to select all the payer id's whose obj_type is only one value and not any other values. In other words, even though each payer has multiple users, I only want the payers who are only using one obj_type.
I have tried using a query like this:
select * from new_table
where obj_type = 1
group by payer_id;
But this returns rows whose payers also have other user's with other obj_types. I am trying to get a result that looks like:
obj | user | payer
----|-------|--------
3 | user1 | payer3
3 | user2 | payer3
Thanks in advance.
That is actually easy:
SELECT player_id
FROM new_table
GROUP BY player_id
HAVING COUNT(DISTINCT obj_type) = 1
Having filters rows just like WHERE but it does so after the aggregation.
The difference is best explained by an example:
SELECT dept_id, SUM(salary)
FROM employees
WHERE salary > 100000
GROUP BY dept_id
This will give you the sum of the salaries of people earning more than 100000 each.
SELECT dept_id, SUM(salary)
FROM employees
GROUP BY dept_id
HAVINF salary > 100000
The second query will give you the departments where all employees together earn more than 100000 even if no single employee earns that much.
If you want to return all rows without grouping them you can use analytic functions:
SELECT * FROM (
SELECT obj_type,user_id,
payer_id,
COUNT(DISTINCT obj_type) OVER (PARTITION BY payer_id) AS distinct_obj_type
FROM new_table)
WHERE distinct_obj_type = 1
Or you can use exist with the query above:
SELECT *
FROM new_table
WHERE payer_id IN (SELECT payer_id
FROM new_table
GROUP BY payer_id
HAVING COUNT(DISTINCT obj_type) = 1)

Selecting a subset of rows with MySql: conditionally limiting number of entries selected

this is a followup question to my previous query. I hope that posting a new question is appropriate in the circumstances: Selecting a subset of rows from a PHP table
I have an sql table that looks like this (for example):
id seller price amount
1 tom 350 500
2 tom 350 750
3 tom 350 750
4 tom 370 850
5 jerry 500 1000
I want to select one row per seller: in particular, for each seller I want the row with the cheapest price, and the largest amount at that price. In the example above, I want rows 2 and 5 (or 3 and 5, I don't care which of 2 and 3 I get as long as I only get one of them).
I am using this:
dbquery("SELECT a.* FROM $marketdb a
INNER JOIN
(
SELECT seller, MAX(amount) amount
FROM $marketdb
WHERE price=$minprice
GROUP BY seller
) b ON a.seller = b.seller AND
a.amount = b.amount;");
But this is giving me rows 2,3 and 5, and I only want one of rows 2 and 3.
I also have a nagging suspicion that this might not always return the minimum price rows either. My tests so far have been confused by the fact that I am getting more than one row with the same amount entered for a given seller.
If someone could point out my error I would be most appreciative.
Thanks!
EDIT: my apologies, I did not ask what I mean to ask. I would like rows returned from the global min price, max 1 per seller, not the min price for each seller. This would be only row 2 or 3 above. Sorry!
Just try adding another group by on seller as you want single row for a seller
to final query like
SELECT a.* FROM $marketdb a
INNER JOIN
(
SELECT seller, MAX(amount) amount
FROM $marketdb
WHERE price=$minprice
GROUP BY seller
)
b ON a.seller = b.seller AND
a.amount = b.amount group by a.seller;
Test this SQL fiddle:
http://sqlfiddle.com/#!2/7de03/2/0
CREATE TABLE `sellers` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`seller` VARCHAR(16) NOT NULL,
`price` FLOAT NOT NULL,
`amount` INT UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `sellers` VALUES (1, 'tom', 350, 500);
INSERT INTO `sellers` VALUES (2, 'tom', 350, 750);
INSERT INTO `sellers` VALUES (3, 'tom', 350, 750);
INSERT INTO `sellers` VALUES (4, 'tom', 350, 850);
INSERT INTO `sellers` VALUES (5, 'jerry', 500, 600);
INSERT INTO `sellers` VALUES (6, 'jerry', 500, 1000);
INSERT INTO `sellers` VALUES (7, 'jerry', 500, 800);
SELECT * FROM
(SELECT DISTINCT * FROM sellers ORDER BY price ASC, amount DESC) t0
GROUP BY seller;
Kind of... works :)
There's an ugly hack at the end of this answer, but if you don't care which row is returned then I guess it saves some typing. Although, if you really don't care which row is returned, that tends to point to a more fundamental flaw in your schema design!
SELECT x.*
FROM market x
JOIN
( SELECT seller,MIN(price) min_price FROM market GROUP BY seller) y
ON y.seller = x.seller
AND y.min_price = x.price
JOIN
( SELECT seller,price,MAX(amount) max_amount FROM market GROUP BY seller,price) z
ON z.seller = y.seller
AND y.min_price = z.price
AND z.max_amount = x.amount
GROUP
BY seller;
Another method, which i dislike but which is popular with others here, goes something like this...
SELECT x.*
FROM
( SELECT *
FROM market
ORDER
BY seller
, price
, amount DESC
, id
) x
GROUP
BY seller;
You may need to GROUP BY the seller column outside of the join. Also, your WHERE clause looks like where price is a set number, instead of <=.
Query:
SQLFIDDLEExample
SELECT s.*
FROM sellers s
WHERE s.id = (SELECT s2.id
FROM sellers s2
WHERE s2.seller = s.seller
ORDER BY s2.price ASC, s2.amount DESC
LIMIT 1)
Result:
| ID | SELLER | PRICE | AMOUNT |
--------------------------------
| 2 | tom | 350 | 750 |
| 5 | jerry | 500 | 1000 |

MySQL sum() on different group bys

Ok, I have a query over two tables. I need to get two sums. I do a group by so the sum() works correctly.
SELECT sum(a.x), sum(b.y) FROM a,b GROUP BY a.n where a.n=b.m
So far this works well, but the problem is i need to group them differently for the second sum (sum(b.y)), than for the first sum (sum(a.x)).
The real query is somewhat more complex but this is my main problem.
This is what i actually try to select sum(stock.amount) - if( sold.amount IS NULL , 0, sum( sold.amount ) )
How can I solve that in one query?
since you are not writing down the tables I am gonna make a wild guess and assume the tables are like :
stock : id, item_id, amount
sold : id, item_id, amount
then again I assume that you need the stock_in_total, sold_total, left_total counts
SELECT
stock_sums.item_id,
stock_sums.st_sum as stock_in_total,
COALESCE(sold_sums.so_sum,0) as sold_total,
(stock_sums.st_sum - COALESCE(sold_sums.so_sum,0)) as left_total
FROM (
SELECT stock.item_id as item_id, SUM(stock.amount) as st_sum
FROM stock
GROUP BY item_id
) as stock_sums
LEFT JOIN (
SELECT sold.item_id as item_id, SUM(sold.amount) as so_sum
FROM sold
GROUP by item_id
) as sold_sums ON stock_sums.item_id = sold_sums.item_id
I hope this would help.
Here is how I would do it. I assume that Stock is the main table, with an ID and an amount, and that Sold maps to Stock via an ID value, and has zero to many records for each Stock item.
SELECT Q1.id, Q1.Total1, Q2.Total2
, Q1.Total1 - COALESCE(Q2.Total2,0) as Outstanding
FROM (
SELECT id, SUM(amount) as Total1
FROM Stock GROUP BY id
) as Q1
LEFT OUTER JOIN (
SELECT id, SUM(Amount) as Total2
FROM Sold GROUP BY id
) as Q2
ON Q2.id = Q1.id
Note that simply formatting your SQL into a clean way forces you to break it into logical parts and will often reveal exactly what is wrong with the query.
The example above also handles correctly the cases where there is not match in the Sold table.
Cheers,
Daniel
(Code Assumptions)
DROP TABLE Stock
CREATE TABLE Stock (
id integer
, amount decimal(10,2)
)
INSERT INTO Stock (id, amount ) VALUES ( 1, 10.1);
INSERT INTO Stock (id, amount ) VALUES ( 2, 20.2);
INSERT INTO Stock (id, amount ) VALUES ( 3, 30.3);
SELECT * FROM STOCK
DROP TABLE Sold
CREATE TABLE Sold (
id integer
, amount decimal(10,2)
)
INSERT INTO Sold (id, amount ) VALUES ( 1, 1.1);
INSERT INTO Sold (id, amount ) VALUES ( 1, 2.2);
INSERT INTO Sold (id, amount ) VALUES ( 1, 3.3);
INSERT INTO Sold (id, amount ) VALUES ( 2, 2.22);
SELECT * FROM Sold
SELECT Q1.id, Q1.Total1, Q2.Total2
, Q1.Total1 - COALESCE(Q2.Total2,0) as Outstanding
FROM (
SELECT id, SUM(amount) as Total1
FROM Stock GROUP BY id
) as Q1
LEFT OUTER JOIN (
SELECT id, SUM(Amount) as Total2
FROM Sold GROUP BY id
) as Q2
ON Q2.id = Q1.id
Results:
id Total1 Total2 Outstanding
1 10.10 6.60 3.50
2 20.20 2.22 17.98
3 30.30 30.30
REVISION
It sounds like you want the total amount of stock you have as one count for each different stock. Then you want how much stock you have left for each stock based on what has been sold. Correct?
If so check this out:
select stock, sum(a.x) as sharesBeforeSale, (sum(a.x) - sum(b.y)) as sharesAfterSale
FROM db.table1 a, db.table2 b
WHERE a.UNIQUEID = b.UNIQUEID AND b.y IS NOT NULL
GROUP BY a.UNIQUEID;
Does that accomplish what you are looking to do?
stock sharesBeforeSale sharesAfterSale
duk 100 25
orc 101 101
yrc 54 41
Enjoy!
Sample tables
db.table1 (stock owned):
UNIQUEID x stock
1 100 duk
2 101 orc
3 54 yrc
db.table2 (stock sold):
UNIQUEID y
1 75
2 0
3 13