I have three tables. I want to write a query to calculate the total handling cost of each order using the tables in MySQL.
create table orders(id integer, packaging varchar(100), delivery
varchar(100));
create table packaging_cost_tbl(packaging_type varchar(100), packaging_cost
integer);
create table delivery_cos_tbl(delivery_type varchar(100), delivery_cost
integer);
insert into orders(id, packaging, delivery) values(1, "Large", "Fast"),(2,
"Small", "Fast"), (3, "Large", "Express"), (4, "Medium", "Standard"), (5,
"Fragile", "Express"), (6, "Medium", "Fast"), (7, "Medium", "Standard");
insert into packaging_cost_tbl(packaging_type, packaging_cost)
values("Small", 2), ("Medium", 5), ("Large", 8), ("Fragile", 10);
insert into delivery_cost_tbl(delivery_type, delivery_cost)
values("Standard", 3), ("Fast", 7), ("Express", 15);
enter image description here
I have mentioned above create table queries and data insert queries for each tables. Output should be,
Order ID
Total handling cost( handling cost=packaging cost + delivery cost)
I'd join the orders table on the two others and sum their costs:
SELECT id, packaging_cost + delivery_cost
FROM orders o
JOIN packaging_cost_tbl p ON o.pacakging = p.packaging_type
JOIN delivery_cost_tbl d ON o.delivery = d.delivery_type
Related
I have two tables
1)LEAD TABLE (which have 3 columns)
Lead_ID || Created_Date || Industry
2)ACCOUNTS TABLE (which have 4 columns)
Account_ID||Created_Date|| Revenue_Range|| Lead_ID
How would I get the average number of days between a lead created and an account created
Don't pay attention to mess in data, I just randomly populated it.
Query returns leadId and difference in days between lead.created_date and account.created_date.
Query:
create table Leads
(
leadId int not null,
created_date datetime,
industry varchar(10),
PRIMARY KEY (leadId)
);
create table Accounts
(
accountId int not null,
created_date datetime,
revenue_range varchar(10),
leadId int not null,
FOREIGN KEY (leadId) REFERENCES Leads(leadId)
);
insert into Leads
values
(1, '2020-01-01', 'a'),
(2, '2020-01-02', 'b'),
(3, '2020-01-03', 'c'),
(4, '2020-02-01', 'd'),
(5, '2020-03-01', 'e');
insert into Accounts
values
(1, '2020-01-03', '1k', 1),
(2, '2020-03-10', '2k', 5),
(3, '2020-02-03', '3k', 2);
select
-- l.leadId,
-- l.created_date as LeadCreatedDate,
-- a.created_date as AccountCreatedDate,
-- ABS is used because it returns with minus sign
AVG(ABS(DATEDIFF(l.created_date, a.created_date))) as AvgDifferenceInDaysBetweenCreation
from Leads as l
inner join Accounts as a
on l.leadId = a.leadId;
You can try it out at SQLize Online
I have two tables, partners and tasks. I would like to write a query that shows all the partners and if there are tasks show only one >= than today (date). It is important that the task is the immediately next to today as date, not the first >= than today that is in the db.
Please find the tables and code below. My problem is that the query does not return the immediately next value to the inserted date, but returns the >= that find in the database.
-- create a table
CREATE TABLE partners
(
id INTEGER PRIMARY KEY,
code INTEGER,
name TEXT
);
CREATE TABLE tasks
(
id INTEGER PRIMARY KEY,
code INTEGER,
comment TEXT,
data INTEGER
);
-- insert some values
INSERT INTO partners VALUES (1, 222, 'M');
INSERT INTO partners VALUES (2, 333, 'F');
INSERT INTO partners VALUES (3, 555, 'F-m');
INSERT INTO tasks VALUES (1, 333, 'f', 2020 );
INSERT INTO tasks VALUES (2, 333, 'f', 2022 );
INSERT INTO tasks VALUES (3, 333, 'f', 2021 );
INSERT INTO tasks VALUES (4, 333, 'f-i', 2023 );
INSERT INTO tasks VALUES (5, 222, 'm', 2021 );
INSERT INTO tasks VALUES (6, 444, 'F', 2025 );
-- fetch some values
SELECT *
FROM partners
LEFT JOIN tasks ON partners.code = tasks.code
WHERE tasks.data >= '2021'
GROUP BY tasks.code
ORDER BY tasks.data ASC;
Desired Result / Query Output:
1 , 222, M, m, 2021
2 , 333, F, f, 2021
3 , 555, F-m, No Task
Consider this schema:
create table Operation(id integer, name varchar(100));
create table Pipeline(operation_in integer, operation_out integer);
Pipeline has foreign keys to Operations, so pipelines are chained, in a way. operation_out is nullable. How do I get the names of operations in both the longest and the shortest pipeline chains using MySQL?
Operations look like this:
INSERT INTO Operation VALUES (1, 'operation one');
While the pipelines look something like this:
INSERT INTO Pipeline VALUES (1, 2);
INSERT INTO Pipeline VALUES (2, 4);
INSERT INTO Pipeline VALUES (4, 7);
INSERT INTO Pipeline VALUES (7, NULL);
I.e. the chain here would be operations with ID 1, 2, 4, 7 and expected result along the lines of:
"operation one", "operation two", "operation four"...
After a few hours of research, I am not sure quite what the solution I am looking for.
Any MySQL version is applicable.
In MySQL 8.x you can use a Recursive CTE to find the chains you need.
For example:
with recursive
a as (
select
p.operation_in,
p.operation_out as current_out,
o.name as op_names,
concat('', p.operation_in) as chain,
1 as size
from pipeline p
join operation o on o.id = p.operation_in
where not exists (
select 1 from pipeline p2 where p2.operation_out = p.operation_in
)
union all
select
a.operation_in,
p.operation_out,
concat(op_names, ', ', o.name),
concat(chain, ',', p.operation_in),
size + 1
from a
join pipeline p on p.operation_in = a.current_out
join operation o on o.id = p.operation_in
),
chains as (
select * from a where current_out is null
)
select op_names, chain, size
from chains
where size = (select max(size) from chains) -- finds the longest one
or size = (select min(size) from chains); -- finds the shortest one
Result:
op_names chain size
--------------------------------- ------- ----
op-nine, op-six 9,6 2
op-one, op-two, op-four, op-seven 1,2,4,7 4
The data script I used is:
create table operation (id integer, name varchar(100));
create table pipeline (operation_in integer, operation_out integer);
insert into operation values (1, 'op-one');
insert into operation values (2, 'op-two');
insert into operation values (4, 'op-four');
insert into operation values (6, 'op-six');
insert into operation values (7, 'op-seven');
insert into operation values (9, 'op-nine');
insert into pipeline values (1, 2);
insert into pipeline values (2, 4);
insert into pipeline values (4, 7);
insert into pipeline values (7, null);
insert into pipeline values (9, 6);
insert into pipeline values (6, null);
I have a schema that requires joining to the same table multiple times to get more information on the data pointed to by the columns. Below is an example schema that shows this situation:
SQL Fiddle: http://sqlfiddle.com/#!9/7a4019/1
CREATE TABLE STATE
(
employee INT NOT NULL,
boss INT,
manager INT,
rep INT
);
CREATE TABLE EMPLOYEE
(
id INT NOT NULL,
name VARCHAR(255) NOT NULL
);
INSERT INTO EMPLOYEE (id, name) VALUES (1, "Joe");
INSERT INTO EMPLOYEE (id, name) VALUES (2, "John");
INSERT INTO EMPLOYEE (id, name) VALUES (3, "Jack");
INSERT INTO EMPLOYEE (id, name) VALUES (4, "Jeff");
INSERT INTO EMPLOYEE (id, name) VALUES (5, "Jason");
INSERT INTO STATE (employee, boss, manager, rep) VALUES (1, 2, 3, 4);
INSERT INTO STATE (employee, boss, manager, rep) VALUES (2, 3, 3, 4);
INSERT INTO STATE (employee, boss, manager, rep) VALUES (3, NULL, NULL, 4);
INSERT INTO STATE (employee, boss, manager, rep) VALUES (4, 3, 3, NULL);
INSERT INTO STATE (employee, boss, manager, rep) VALUES (5, 2, 3, 4);
Currently, the only way i know to get this information in single rows for each employee, is left joining multiple times like this:
SELECT employee, b.name AS boss, m.name AS manager, r.name AS rep
FROM STATE
LEFT JOIN EMPLOYEE b ON b.employee = STATE.boss
LEFT JOIN EMPLOYEE m ON m.employee = STATE.manager
LEFT JOIN EMPLOYEE r ON r.employee = STATE.rep
Is there a way to do it without joins and without subqueries?
You asked:
Is there a way to do it without joins and without subqueries?
Not really. You are using the JOIN operations precisely as they're intended to be used -- each JOIN reflects a specific relationship between rows of a table.
You can avoid doing multiple joins by using aggregate functions, which I recently found useful after hitting the limit (61) of the number of joins that can be done in a query in MySQL/MariaDB.
SELECT s.employee,
GROUP_CONCAT(if(e.id=s.boss, name, NULL)) as boss,
GROUP_CONCAT(if(e.id=s.manager, name, NULL)) as manager,
GROUP_CONCAT(if(e.id=s.rep, name, NULL)) as rep,
FROM STATE s, EMPLOYEE e
GROUP BY s.employee
The above example uses MySQL's GROUP_CONCAT function. It appears not to be an ANSI standard. Other relational databases may have similar functions. A cursory web search turned up a page that discussed aggregate functions for various relational databases: http://www.postgresonline.com/journal/archives/191-String-Aggregation-in-PostgreSQL,-SQL-Server,-and-MySQL.html
I am still an sql greenhorn and try to convert this script, building a running total as view in mysql:
DROP TABLE IF EXISTS `table_account`;
CREATE TABLE `table_account`
(
id int(11),
account int(11),
bdate DATE,
amount DECIMAL(10,2)
);
ALTER TABLE `table_account` ADD PRIMARY KEY(id);
INSERT INTO `table_account` VALUES (1, 1, '2014-01-01', 1.0);
INSERT INTO `table_account` VALUES (2, 1, '2014-01-02', 2.1);
INSERT INTO `table_account` VALUES (4, 1, '2014-01-02', 2.2);
INSERT INTO `table_account` VALUES (5, 1, '2014-01-02', 2.3);
INSERT INTO `table_account` VALUES (3, 1, '2014-01-03', 3.0);
INSERT INTO `table_account` VALUES (7, 1, '2014-01-04', 4.0);
INSERT INTO `table_account` VALUES (6, 1, '2014-01-06', 5.0);
INSERT INTO `table_account` VALUES (8, 1, '2014-01-07', 6.0);
SET #iruntot:=0.00;
SELECT
q1.account,
q1.bdate,
q1.amount,
(#iruntot := #iruntot + q1.amount) AS runningtotal
FROM
(SELECT
account AS account,
bdate AS bdate,
amount AS amount
FROM `table_account`
ORDER BY account ASC, bdate ASC) AS q1
This is much more faster than building a sum over the whole history on each line.
The problems I cannot solve are:
Set in view
Subquery in view
I think it might be posssible to use some kind of JOIN instead of "SET #iruntot:=0.00;"
and use two views to prevent the need of a subquery.
But I do know how.
Will be happy for any hints to try.
Regards,
Abraxas
MySQL doesn't allow subqueries in the from clause for a view. Nor does it allow variables. You can do this with a correlated subquery, though:
SELECT q.account, q.b_date, q.amount,
(SELECT SUM(q2.amount)
FROM myview1 q2
WHERE q2.account < q.account OR
q2.account = q.account and q2.date <= q.date
) as running total
FROM myview1 q;
Note that this assumes that the account/date column is unique -- no repeated dates for an account. Otherwise, the results will not be exactly the same.
Also, it seems a little strange that you are doing a running total across all accounts and dates. I might expect a running total within accounts, but this is how you formulated the query in the question.