Joining tables to create a total sum of two seperate orders - sql

I am trying to get the cost of each item over to a final table where it only shows the final ORDER_ID and the ORDER_TOTAL. I am having issues getting my two tables to join and am struggling with JOIN as a whole. These are the two tables that I am trying to join.
ORDER COST
CREATE TABLE `order cost` (
`ORDER_ID` int(10) NOT NULL,
`MENU_COST` double(5,2) NOT NULL,
`ORDER_COST` double(5,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `order cost` (`ORDER_ID`, `MENU_COST`, `ORDER_COST`) VALUES
(1, 7.00, 7.63),
(3, 8.00, 8.72),
(1, 13.00, 14.17),
(3, 25.00, 27.25);
ALTER TABLE `order cost`
ADD PRIMARY KEY (`ORDER_COST`),
ADD KEY `ORDER_ID` (`ORDER_ID`),
ADD KEY `MENU_COST` (`MENU_COST`);
ORDER TOTAL
CREATE TABLE `order total` (
`ORDER_ID` int(11) NOT NULL,
`ORDER_TOTAL` double(6,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `order total` (`ORDER_ID`, `ORDER_TOTAL`) VALUES
(1, 0.00),
(3, 0.00);
ALTER TABLE `order total`
ADD KEY `ORDER_ID` (`ORDER_ID`);
My join code attempt:
SELECT ORDER_ID,
SUM(MENU_COST) = ORDER_TOTAL
FROM `order cost`
INNER JOIN `order total`
GROUP BY ORDER_ID;
I have tried multiple other join ideas, and in the main order cost table I was able to get the SUMS(ORDER_COST) to create its own cell after I ran a
SELECT ORDER_ID, SUM(MENU_COST) FROM `order cost` GROUP BY ORDER_ID
But this creates its own table after the run, whereas I am looking to get the table to auto produce in the 'order total' based on changes to the database.

See comments above for my thoughts on not doing this. You'll have to run this query every time you make a change to the ordercost table:
UPDATE
ordertotal
SET
order_total = (
SELECT SUM(MENU_COST) FROM ordercost WHERE ORDER_ID = x GROUP BY ORDER_ID
)
WHERE order_id = x
There is no joining here.. you insert some new item to ordercost and then run the query to sync the table. On the whole it's a terrible idea. You should consider instead creating a VIEW and selecting from it when needed:
CREATE VIEW ordertotal AS
SELECT
ORDER_ID,
SUM(MENU_COST) as ORDER_TOTAL
FROM ordercost
GROUP BY ORDER_ID
It will behave like a table:
SELECT * FROM ordertotal where order_id = 3

Related

Group by count multiple tables

Need to find out why my group by count query is not working. I am using Microsoft SQL Server and there are 2 tables I am trying to join.
My query needs to bring up the number of transactions made for each type of vehicle. The output of the query needs to have a separate row for each type of vehicle such as ute, hatch, sedan, etc.
CREATE TABLE vehicle
(
vid INT PRIMARY KEY,
type VARCHAR(30) NOT NULL,
year SMALLINT NOT NULL,
price DECIMAL(10, 2) NOT NULL,
);
INSERT INTO vehicle
VALUES (1, 'Sedan', 2020, 240)
CREATE TABLE purchase
(
pid INT PRIMARY KEY,
vid INT REFERENCES vehicle(vid),
pdate DATE NOT NULL,
datepickup DATE NOT NULL,
datereturn DATE NOT NULL,
);
INSERT INTO purchase
VALUES (1, 1, '2020-07-12', '2020-08-21', '2020-08-23')
I have about 10 rows on information in each table I just haven't written it out.
This is what I wrote but it doesn't return the correct number of transactions for each type of car.
SELECT
vehicle.vid,
COUNT(purchase.pid) AS NumberOfTransactions
FROM
purchase
JOIN
vehicle ON vehicle.vid = purchase.pid
GROUP BY
vehicle.type;
Any help would be appreciated. Thanks.
Your GROUP BY and SELECT columns are inconsistent. You should write the query like this:
SELECT v.Type, COUNT(*) AS NumPurchases
FROM Purchase p JOIN
Vehicle v
ON v.vID = p.pID
GROUP BY v.Type;
Note the use of table aliases so the query is easier to write and read.
If this doesn't produce the expected values, you will need to provide sample data and desired results to make it clear what the data really looks like and what you expect.

SQL create a full part list out of a given part list (iteration)

I have a problem with a given task from my SQL lecture.
In the task there is a database given with a list of parts of three robots and another list, where these parts are linked to parts from the first list, that they are made of, if they are made of other parts.
The databasess can be generated with:
CREATE TABLE part (
part_id BIGINT PRIMARY KEY,
part_namevarchar(64)NOTNULL
);
CREATE TABLE part_part (
object_id BIGINT,
part_id BIGINT,
quantity INT NOT NULL,
PRIMARY KEY(object_id, part_id)
);
ALTER TABLE part_part
ADD CONSTRAINT part_id_fkey FOREIGNKEY (part_id) REFERENCES part(part_id)
ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE part_part
ADD CONSTRAINT object_id_fkey FOREIGN KEY(object_id)REFERENCES part(part_id)
ON UPDATE CASCADE ONDELETE CASCADE;
INSERT INTO part(part_id, part_name)
VALUES
(0,'CPU A'),
(1,'Cables'),
(2,'Motherboard 3xy'),
(3,'Motor ayX'),
(4,'Arm'),
(5,'Body'),
(6,'Leg'),
(7,'Wheel'),
(8,'Motherboard 7ax'),
(9,'Joint'),
(10,'Motor Z1238'),
(11,'Hammer'),
(12,'Screw A'),
(13,'Screw B'),
(14,'Screw C'),
(15,'Robo 1000'),
(16,'CPU B'),
(17,'CPU C'),
(18,'Robo 2000'),
(19,'Screwdriver'),
(20,'Robo 3000');
INSERT INTO part_part
(object_id, part_id, quantity)
VALUES
(5,2,1),
(5,0,1),
(5,3,2),
(5,1,5),
(5,12,3),
(4,9,3),
(4,10,3),
(4,13,13),
(6,3,2),
(6,7,4),
(15,4,2),
(15,11,2),
(15,5,1),
(15,6,1),
(18,4,2),
(18,11,2),
(18,5,1),
(18,6,2),
(18,16,1),
(20,4,3),
(20,11,1),
(20,19,1),
(20,5,1),
(20,6,1),
(20,16,1),
(20,17,1);
Now the task is to get the list of all parts and subparts needed for the "Robo 3000" and their quantity.
I got as far as:
WITH part2(part_name1, subpart_id, top_quantity, top_part_id, part_name) AS(
WITH part1(part_name, subpart_id, quantity) AS(
WITH subpart1(object_id, subpart_id, quantity) AS(SELECT * FROM part_part)
SELECT part_name, subpart_id, quantity FROM subpart1
JOIN part ON part.part_id = subpart1.object_id
WHERE part_name = 'Robo 3000'
)
SELECT * FROM part1
JOIN part ON part1.subpart_id = part.part_id
)
SELECT * FROM part2
JOIN part_part ON part2.top_part_id = part_part.object_id
ORDER BY top_part_id;
Which gives me a list of only subparts (the parts of all the parts from the robot, that need parts themselves) and also it doesn't consider, if parts are used multiple times, here the arm is used 3 times but its parts aren't multiplied with the quantity.
Also this is limited, since it only looks at the given part and the supp parts but not deeper if needed.
Is there a way to iterate through all the parts and make them into a big list in SQL?
The same way for example a java method would with a self calling method?
PostgreSQL supports recursive sQL which may be one way solve your problem. Below is a example using your data.
with recursive part_list as
(
(
select object_id as unit, object_id, part_id, quantity, quantity as totqty
from part_part
where object_id = 20
)
union
select pl.unit, pp.object_id, pp.part_id, pp.quantity, pp.quantity * pl.quantity
from part_part pp
join part_list pl
on pp.object_id = pl.part_id
)
select u.part_name as unit,
part.part_name,
sum(part_list.totqty) as total_parts
from part_list
join part u
on u.part_id = part_list.unit
join part
on part.part_id = part_list.part_id
group by u.part_name, part.part_name
order by 1,3;

Unique constraint in Postgres based on last non-null value

I have a table that looks like the following: create table prices_history (id0 serial primary key, product_id0 int, time_added timestamptz, listed_price numeric)
I would like to only insert a new price for a particular product_id0 into the table when max(time_added) of that product_id0 is distinct from the price I'm about to insert. Currently, I'm doing this through the following query, assuming that I want to insert a price of 9.50 for the product with id 101:
insert into prices_history (product_id0, time_added, price)
(
select 101, NOW(), 9.50 where not exists (
select * from (
select distinct on (product_id0) * from prices_history order by product_id0, time_added desc
) x where product_id0=101 and listed_price=9.50
)
) returning id0
Is there a better query to solve this problem?
I'm using Postgres v9.6.8 on Ubuntu 16.04 LTS.
Instead of using WHERE NOT EXISTS, I found that a more sustainable solution to do bulk inserts was LEFT JOIN..WHERE NULL. This involves doing a left join with the data I wanted to insert, choosing the data where there was no matching data in the old table. In the following example, say I had the following pricing data (represented as JSON):
[{price: 11.99, product_id0:2},
{price: 10.50, product_id0:3},
{price: 10.00, product_id0:4}]
The following query, would insert a subset of this data if any of it is new information:
insert into prices_history (product_id0, time_added, price)
(
select new_product_id0, new_time_added, new_price from
(select unnest(array[11.99, 10.50, 10.00]) as new_price, unnest(array[2,3,4]) as new_product_id0, now() as new_time_added) new_prices left join
(select distinct on (product_id0) * from prices_history order by product_id0, time_added desc) old_prices
on old_prices.product_id0 = new_prices.new_product_id0 and old_prices.listed_price= new_prices.new_price
where old_prices.product_id0 is null and old_prices.listed_price is null
) returning id0
This new query seems to work well in the current deployment.

SQL Server 2008 R2: Show only recently added records

I have two tables:
Cust : Contains customer details like customer ID and customer Name.
Cust_Address : This table contains customer ID and customer address.
Table: Cust
create table cust
(
cust_id int,
cust_name varchar(10)
);
Records Insertion:
insert into cust values(1,'A');
insert into cust values(2,'B');
insert into cust values(3,'C');
insert into cust values(4,'D');
Table: Cust_Address
create table cust_address
(
cust_id int,
cust_add varchar(50)
);
Records Insertion:
insert into cust_address values(1,'US');
insert into cust_address values(2,'UK');
insert into cust_address values(3,'UAE');
insert into cust_address values(4,'SA');
insert into cust_address values(1,'AUS');
insert into cust_address values(2,'IND');
insert into cust_address values(3,'SL');
insert into cust_address values(1,'CHINA');
Now I want to show the result which contains the latest customer address which have been inserted in the table Cust_Address.
Expected Result:
Cust_ID Cust_Name Cust_Add
-------------------------------
1 A CHINA
2 B IND
3 C SL
4 D SA
Here is the SQLFiddle for tables and its records.
You are not able to retrieve the rows in any particular order. You need some more info to get an order.
The best way is primary index in Cust_address
CustAddrID int identity(1, 1) not null primary key
You can also have a CreatedOn column that will have default value equal to getDate()
After that you can figure out what is the last inserted value for CustAddr for each Cust record.
In case you are not able to add new column there then maybe
change tracking functionality. But your issue seems to be too trivial for that.
There are also Temporal Tables in SQL Server 2016. But again it's probably too much.
Here is an example how you can get the address using primary key CustAddrID
SQL Fiddle
select cust_name, cust_add
from cust C
join
(select
cust_add, cust_id,
row_number() over (partition by cust_id order by cust_add_id desc) rn
from cust_address ) CLA
on CLA.cust_id = C.cust_id and
CLA.rn = 1
Identity column increases every time when we insert new value to the table. The correct value for your case will be the record with the highest cust_add_id and specified cust_id.
In the above query we generates numbers in desc order starting from 1 using row_number() function for each cust_id (partition by cust_id). Finally we take only the records with generated number rn equal to 1 CLA.rn = 1 and we join it to cust table.
You can replace row_number() by max(cust_add_id) and group by cust_id. However in that case you need to join cust_add table twice.
You will not be able to get the rows out of the link table in the order they were inserted.
You need to have a column for this.
Imagine how big the meta-data would be if you needed to keep a record for each record for creation! Would you also want to keep meta-data on your meta-data so you know when the meta-data was updated? The space use can quickly escalate.
SQL Server keeps some stats but something this specific will need to come from a user-defined field.
So you either use a identity column in the CustAddr table [CustAddr int identity(1, 1) not null primary key] or add a column for createdDateAndTime DateTime Default GetDate().

How to find the need of materials from nested estimates in Postgres

Product estimates contain sub-products.
Sub-products can contain also sub-products etc.
Finally tree leafs contians materials.
Maximum nesting level is 10.
Orders contain also products, sub-products and materials with ordered quantities.
How to find the need of materials required to fullfill the orders?
Products, sub-products and materials are in single table:
create table toode (productid char(10) primary key );
Estimate table:
create table dok (
dokumnr serial primary key,
productid char(10) not null references toode
);
Sub-products and materials in estimates:
create table rid (
id serial primary key,
dokumnr int not null references dok,
itemid char(10) not null references toode,
quantity numeric(12,4) -- quantity required to make one product
);
Orders:
create table orderrows (
id serial primary key,
itemid char(10) not null references toode,
quantity numeric(12,4) -- ordered quantity
);
Result should be query which return the need of materials and sub-products:
itemid char(10) not null references toode,
requiredquantity numeric(12,4) -- total quantity of items required to make ordered products
How to implement this in Postgresql 9.2?
Described fields should remain in those tables. It is possible to add additional
columns and tables if this helps.
Is it possible to make some universal query which works with unilimited nesting level.
Or is it best way to create query which repeats some parts 10 times for maximum nensting level ?
Update
estimates
product1
material1 2 pcs
subproduct2 3 pcs
subproduct2
material2 4 pcs
are described as
insert into dok values (1,'product1');
insert into rid (dokumnr, itemid, quantity) values (1, 'material1', 2);
insert into rid (dokumnr, itemid, quantity) values (1, 'subproduct2', 3);
insert into dok values (2,'subproduct2');
insert into rid (dokumnr, itemid, quantity) values (2, 'material2', 4);
If 10 pieces of product1 are ordered this is described as:
insert into orderrows (itemid, quantity ) values ('product1', 10);
Result should be:
material1 20
material2 120
material1 quantity is calculated as 10*2.
material2 quantity is calculated as 10*3*4
Update 2
Joachim answer gives incorrect result on multi level estimates when last level contains more that one row. Last join LEFT JOIN rid rid2 ON rid2.dokumnr = dok2.dokumnr returns multiple rows and result table is duplicated.
Testcase http://sqlfiddle.com/#!12/e5c11/1/0 :
create table toode (productid char(15) primary key );
create table dok (
dokumnr serial primary key,
productid char(15) not null references toode
);
create table rid (
id serial primary key,
dokumnr int not null references dok,
itemid char(15) not null references toode,
quantity numeric(12,4) -- quantity required to make one product
);
create table orderrows (
id serial primary key,
itemid char(15) not null references toode,
quantity numeric(12,4) -- ordered quantity
);
INSERT INTO toode VALUES ('product1'),('material1'),('subproduct2'), ('material2'), ('material3');
insert into dok values (1,'product1');
insert into dok values (2,'subproduct2');
insert into rid (dokumnr, itemid, quantity) values (1, 'material1', 1);
insert into rid (dokumnr, itemid, quantity) values (1, 'subproduct2', 1);
insert into rid (dokumnr, itemid, quantity) values (2, 'material2', 1);
insert into rid (dokumnr, itemid, quantity) values (2, 'material3', 1);
insert into orderrows (itemid, quantity ) values ('product1', 1);
Expected:
Every quantity is 1 so result quantity must be 1 for every material.
Observed:
Material2 and matererial3 rows are duplicated.
How to fix this ? Query should determine leaf nodes itself. Leaf nodes are not marked specially in data.
This should do it using a recursive query;
WITH RECURSIVE t(itemid,qty) AS (
SELECT itemid,quantity,false isleaf FROM orderrows
UNION ALL
SELECT rid.itemid,(rid.quantity*t.qty)::NUMERIC(12,4),
dok2.productid IS NULL
FROM t
JOIN dok ON dok.productid=t.itemid
JOIN rid ON rid.dokumnr=dok.dokumnr
LEFT JOIN dok dok2 ON dok2.productid=rid.itemid
)
SELECT itemid, SUM(qty) FROM t WHERE isleaf GROUP BY itemid
An SQLfiddle to test with.
Try this query:
;with recursive cte as (
select r.itemid, r.quantity * o.quantity as quantity, false as is_material
from orderrows as o
inner join dok as d on d.productid = o.itemid
inner join rid as r on r.dokumnr = d.dokumnr
union
select r.itemid, r.quantity * o.quantity as quantity, itemid like 'material%'
from cte as o
inner join dok as d on d.productid = o.itemid
inner join rid as r on r.dokumnr = d.dokumnr
)
select * from cte as c
where c.itemid not in (select t.productid from dok as t);
here's SQL FIDDLE example to test it. Here I'm assuming that your define materials as products which name starts with 'material', but I think that you should have an attribute is_material or something like that in your DB, so you could change this condition.
update - test case sql fiddle