SQL invoice three tables into one query - mysql

Hello,
I have 3 tables:
CREATE TABLE `invoice` (
`id` int(11) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `invoice` (`id`) VALUES
(1),
(2),
(3);
CREATE TABLE `invoice_deduction` (
`id` int(11) NOT NULL,
`invoiceId` int(11) NOT NULL,
`deductionId` int(11) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (`invoiceId`) REFERENCES `invoice` (`id`),
FOREIGN KEY (`deductionId`) REFERENCES `invoice` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `invoice_deduction` (`id`, `invoiceId`, `deductionId`) VALUES
(1, 2, 1),
(2, 3, 1),
(3, 3, 2);
CREATE TABLE `invoice_item` (
`id` int(11) NOT NULL,
`invoiceId` int(11) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (`invoiceId`) REFERENCES `invoice` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `invoice_item` (`id`, `invoiceId`) VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 2),
(5, 2),
(6, 3),
(7, 3),
(8, 3);
For example:
The first invoice contains 3 items (total: 3 items)
The second invoice contains 3 items from first invoice (deduction) and 2 new items (total: 5 items)
The third invoice contains 3 items from first invoice (deduction), 2 items from second invoice (deduction) and 3 new items (total: 8 items)
So I want to have a query with this result:
id | count of items (with deductions)
3 | 8
2 | 5
1 | 3
I start with this query:
SELECT
i.id, COUNT(*) as countItems
FROM
invoice i JOIN invoice_item it ON i.id = it.invoiceId
GROUP BY
it.invoiceId
ORDER BY
countItems
DESC
I thank you in advance for your help.

I think you want a cumulative sum:
SELECT i.id, COUNT(*) as countItems,
SUM(COUNT(*)) OVER (ORDER BY it.id)
FROM invoice i JOIN
invoice_item it
ON i.id = it.invoiceId
GROUP BY i.id
ORDER BY countItems;
I have no idea why you are using a LEFT JOIN. I don't know why you would have invoiceId values that do not match a valid invoice. So I switched it to an inner join.

Related

Category and subcategory recursive query SQL

I got a little problem with my recursive query.
I got a database of menu of a bar.
We got: Category, each category got sub-categories and each-subcategories got multiple items.
The database is this one and the query is linked inside:
CREATE TABLE category (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
title varchar(255) NOT NULL,
parent_id int(10) unsigned DEFAULT NULL,
PRIMARY KEY (id),
FOREIGN KEY (parent_id) REFERENCES category (id)
ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE `items` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`cat_id` int unsigned DEFAULT NULL,
`parent_id` int unsigned DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `cat_id` (`cat_id`),
KEY `sub_id` (`parent_id`),
CONSTRAINT `cat_id` FOREIGN KEY (`cat_id`) REFERENCES `category` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `sub_id` FOREIGN KEY (`parent_id`) REFERENCES `category` (`parent_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
BEGIN;
INSERT INTO `category` VALUES (1, 'Colazione', NULL);
INSERT INTO `category` VALUES (2, 'Pranzo', NULL);
INSERT INTO `category` VALUES (3, 'Primi piatti', 2);
INSERT INTO `category` VALUES (4, 'Second dish', 2);
INSERT INTO `category` VALUES (5, 'Other things for lunch', 2);
COMMIT;
-- ----------------------------
-- Records of items
-- ----------------------------
BEGIN;
INSERT INTO `items` VALUES (1, 1, NULL, 'Cornetto');
INSERT INTO `items` VALUES (2, 3, 2, 'Pasta al sugo 1');
INSERT INTO `items` VALUES (3, 3, 2, 'Pasta al sugo 2');
INSERT INTO `items` VALUES (4, 3, 2, 'Pasta al sugo 3');
INSERT INTO `items` VALUES (5, 3, 2, 'Pasta al sugo 1 X');
INSERT INTO `items` VALUES (6, 3, 2, 'Pasta al sugo 2 X');
INSERT INTO `items` VALUES (7, 4, 2, 'Pasta al sugo 3 X');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
Query:
with combine_trees as (
with make_tree as (
WITH RECURSIVE category_path AS
(
SELECT id, title, parent_id
FROM category
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.title, c.parent_id
FROM category_path AS cp JOIN category AS c
ON cp.id = c.parent_id
)
SELECT cp.title, cp.id,
if(cp.id = category.id,
json_arrayagg(json_object('item_name', it.name)),
json_object(cp.title, json_object('items',json_arrayagg(json_array(json_object('item_name', it.name))))))
as tree
FROM category_path cp
INNER JOIN items it ON it.cat_id = cp.id
join category on category.id = ifnull(cp.parent_id, cp.id)
group by cp.title, cp.id, category.id
)
select json_arrayagg(json_object(title, json_array('items', tree))) output_json from make_tree group by id
)
select json_object('menu',group_concat(output_json)) as output from combine_trees;
https://sqlize.online/
The problem is that its not printing the result as JSON but its printing it formatted in one-string. How can we transform it in a JSON without that all the output is an unique string?
In your last line,
select json_object('menu',group_concat(output_json)) as output from combine_trees
you cannot use group_concat to combine the json arrays you get from the 2nd to last line (e.g. select json_arrayagg(...) output_json from make_tree group by id).
The arrays you get there each look like [...], and group_concat will give you [...], [...]. This is not a valid json array (which would need additional brackets around it, e.g. [[...],[...]]), but a string, and creating a json object from it will give you, well, that string as the value.
To combine your json arrays, you can use (as you did before) json_arrayagg instead of group_concat, e.g.
select json_object('menu',json_arrayagg(output_json)) as output from combine_trees

Counting a left join depending on other left join

I have this query:
SELECT options.id, options.text,
COUNT(options2.id) AS num_children
FROM options
JOIN history_uids AS uids
ON uids.uid = options.id
LEFT JOIN options AS options2
ON (options.id = options2.id_parent)
LEFT JOIN history_uids AS uids1
ON uids1.uid = options2.id
WHERE (
options.id = 25
AND (uids1.active = 1 OR
options2.id IS NULL) # Problem
)
GROUP BY `options`.`id`
There is an options table, and another, history_uids, which has a column named uid with the id of every options, and an active column set to 1 or 0.
I am expecting to get a result with:
The ID and the text of the option which has the id 25, and which has active set to 1 in history_uids.
The number of options which have an id_parent equal to the id (25) and for which active in history_uids is set to 1
So whatever is this number I should get my row if it has active set to 1. I cannot understand how to achieve this: I wanna set as last condition uids1.active = 1 OR "options2 doesn't exist", but to get this I should have my active condition in the ON of options2, which is not possible because at that moment the table history_uids is not yet referenced...
In my case the row has active set to 1 and has 5 children with active set to 0, so I should get my row with num_children set at 0. Instead whatever combination I do in the JOIN or the WHERE I get either num_children set to 5 or no result at all.
Thanks for your help (and for reading!)
Here is the full structure and data for testing:
CREATE TABLE `history_uids` (
`uid` int(10) NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `history_uids` (`uid`, `active`) VALUES
(1, 1),
(2, 0),
(3, 0),
(4, 0),
(5, 0),
(6, 0);
CREATE TABLE `options` (
`id` int(10) NOT NULL,
`id_parent` int(10) DEFAULT NULL,
`text` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `options` (`id`, `id_parent`, `text`) VALUES
(1, NULL, 'parent active'),
(2, 1, 'child 1 inactive'),
(3, 1, 'child 2 inactive'),
(4, 1, 'child 3 inactive'),
(5, 1, 'child 4 inactive'),
(6, 1, 'child 5 inactive');
ALTER TABLE `history_uids`
ADD PRIMARY KEY (`uid`);
ALTER TABLE `options`
ADD PRIMARY KEY (`id`),
ADD KEY `id_parent` (`id_parent`);
ALTER TABLE `options`
ADD CONSTRAINT `ibfk_3` FOREIGN KEY (`id_parent`) REFERENCES `options` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `ibfk_4` FOREIGN KEY (`id`) REFERENCES `history_uids` (`uid`) ON DELETE CASCADE ON UPDATE CASCADE;
You need to put the active test for the child row into the ON clause, so that inactive rows will be filtered out of the LEFT JOIN. And you should be counting uids1.uid, not options2.id, since the inactive rows aren't filtered out until you join with uids1.
SELECT options.id, options.text,
COUNT(uids1.uid) AS num_children
FROM options
JOIN history_uids AS uids
ON uids.uid = options.id
LEFT JOIN options AS options2
ON (options.id = options2.id_parent)
LEFT JOIN history_uids AS uids1
ON uids1.uid = options2.id and uids1.active = 1
WHERE
uids.active = 1
GROUP BY `options`.`id`
DEMO

SQL - how to get all subcategory products incl. self products

I have a problem with my database and I hope you can help me with this.
I have tables like this:
categories
--------------------
|id | name | parent|
____________________
categories_x_products (m:n)
---------------------
|id | ctg_id | p_id |
---------------------
products
------------
| id, name |
------------
And my question is: "how to get all my & subcategories product count?"
For example:
categories:
id = 1, name = computers, parent = 0 (1 product)
id = 2, name = notebooks, parent = 1 (2 products)
and I want to get
computers : 3
notebooks : 2
I try this but does not working
select a.name, count(b.id)
FROM categories a
LEFT JOIN categories x ON x.id=a.parent
LEFT JOIN category_x_product b ON b.ctg_id=a.id
group by a.id
Thank you for answers.
Here is some example data:
-- Adminer 4.2.3 MySQL dump
SET NAMES utf8;
SET time_zone = '+00:00';
SET foreign_key_checks = 0;
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
DROP TABLE IF EXISTS `categories`;
CREATE TABLE `categories` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`parent` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `categories` (`id`, `name`, `parent`) VALUES
(1, 'computers', 0),
(2, 'notebooks', 1),
(3, 'lenovo', 2);
DROP TABLE IF EXISTS `products`;
CREATE TABLE `products` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `products` (`id`, `name`) VALUES
(1, 'lenovo thinkpad 001'),
(2, 'lenovo thinkpad 002'),
(3, 'lenovo thinkpad 003'),
(4, 'lenovo thinkpad 004'),
(5, 'lenovo thinkpad 005'),
(6, 'random comp.');
DROP TABLE IF EXISTS `product_x_category`;
CREATE TABLE `product_x_category` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`product_id` int(11) DEFAULT NULL,
`category_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `product_x_category` (`id`, `product_id`, `category_id`) VALUES
(1, 1, 3),
(2, 2, 3),
(3, 3, 3),
(4, 4, 3),
(5, 5, 3),
(6, 6, 1);
-- 2016-02-23 08:16:30
I've tried to run your SQL Query (Create table and insert value).
And I run this query
SELECT a.name, COUNT( b.id )
FROM categories a
LEFT JOIN product_x_category b ON b.category_id = a.id
GROUP BY a.id
And it returns this
This is your product_x_Category table (where category_id --> 3 = lenovo and category_id --> 1 = computer)
I think the result is what you want, isn't it?

Function to find first available option based on count of records and condition

I need to write an SQL statement to get the first 'free' poule (pool / collection of teams) for my team. Let's explain a bit.
I have two tables, one table poules with 4 poules each having a TEAMQTY of 4 (the max. number of teams allowed in a poule):
ID TOURNID NAME TEAMQTY
1 1 Poule 1 4
2 1 Poule 2 4
3 1 Poule 3 4
4 1 Poule 4 4
and a table teams
ID TOURNID NAME POULEID
1 1 Team 1 1
2 1 Team 2 1
3 1 Team 3 1
4 1 Team 4 1
I want to write a function in mysql which based on the situation above suggest a pouleid of 2 since poule 1 is completely filled up with teams. IOW I should be able to insert 4 more teams in PouleId 2, after that my function should return PouleID 3 as a suggestion.
I'm new to mysql (an sql noob) and I've tried:
SELECT id FROM POULES WHERE TOURNID = 1 AND
teamqty > (SELECT COUNT(ID) FROM TEAMS WHERE TOURNID = 1) LIMIT 1
Needless to say my experiment sql code is useless..
Do I need a while loop here or would an SQL statement do?
Here's my supporting code:
CREATE TABLE IF NOT EXISTS `poules` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`TOURNID` int(11) NOT NULL,
`NAME` varchar(20) NOT NULL,
`TEAMQTY` int(11) NOT NULL,
PRIMARY KEY (`ID`),
KEY `TOURNID` (`TOURNID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
INSERT INTO `poules` (`ID`, `TOURNID`, `NAME`, `TEAMQTY`) VALUES
(1, 1, '1', 4),
(2, 1, '2', 4),
(3, 1, '3', 4),
(4, 1, '4', 4);
CREATE TABLE IF NOT EXISTS `teams` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`TOURNID` int(11) NOT NULL,
`NAME` varchar(50) NOT NULL,
`POULEID` int(11) DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `NAME` (`NAME`),
KEY `TOURNID` (`TOURNID`))
ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;
INSERT INTO `teams` (`ID`, `TOURNID`, `NAME`, `POULEID`) VALUES
(1, 1, '1', 1),
(2, 1, '2', 1),
(3, 1, '3', 1),
(4, 1, '4', 1);
TIA Mike
you can do left join with a subquery that gets total team count and compares with team count in the main table
you can use limit to get the one result based on order by on team count.
select p.id as pouleid, ifnull(t.teamcount,0), p.tournid
from poules p
left join ( select count(pouleid) as teamcount, pouleid, tournid
from teams
group by pouleid, tournid
)t
on p.id = t.pouleid
and p.tournid = t.tournid
where ifnull(t.teamcount,0) < p.teamqty

MySQL INNER JOIN of 3 tables with count and totals

I have the following sample database set up -
CREATE TABLE IF NOT EXISTS `companies`(
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`company` varchar(75) NOT NULL,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `companies` (`id`, `company`) VALUES
(1, 'Acme Widget Company'),
(2, 'Intrepid Inc.'),
(3, 'Allied Corp.');
CREATE TABLE IF NOT EXISTS `companies_customers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`customer_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
INSERT INTO `companies_customers` (`id`, `company_id`, `customer_id`) VALUES
(1, 2, 1),
(2, 2, 2),
(3, 2, 4),
(4, 1, 3),
(5, 1, 1);
CREATE TABLE IF NOT EXISTS `customers` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`firstname` varchar(25) NOT NULL,
`lastname` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
INSERT INTO `customers` (`id`, `firstname`, `lastname`) VALUES
(1, 'John', 'Smith'),
(2, 'Sue', 'Jones'),
(3, 'David', 'Flanders'),
(4, 'Kathy', 'Freeman');
CREATE TABLE IF NOT EXISTS `orders` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`customer_id` int(11) NOT NULL,
`amount` decimal(10,0) NOT NULL,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
INSERT INTO `orders` (`id`, `customer_id`, `amount`) VALUES
(1, 1, 500),
(2, 3, 1000),
(3, 1, 250),
(4, 4, 800),
(5, 4, 100);
I need to write a query which retrieves a list of all company names, a count of the number of customers in each company, and a sum of the customers orders in each company, like this -
Company Total Customers All Orders Total
Acme Widget Company 2 750
Intrepid Inc. 3 1650
Allied Corp. 0 0
I nearly have it resolved with the following SQL -
SELECT company AS 'Company', customersCount AS 'Total Customers', customerOrdersTotal AS 'All Orders Total'
FROM
( SELECT cc.customer_id, SUM(innerQuery.ordersTotal) customerOrdersTotal
FROM (SELECT cu.id customerId, SUM(amount) ordersTotal
FROM customers cu
JOIN orders o ON o.customer_id = cu.id
GROUP BY customerId
) innerQuery
JOIN companies_customers cc ON innerQuery.customerId = cc.customer_id
GROUP BY cc.customer_id
) inner_1
RIGHT JOIN
( SELECT cc.id, c.company, COUNT(*) customersCount
FROM companies c
JOIN companies_customers cc ON c.id = cc.company_id
GROUP BY c.id
) inner_2
ON inner_1.customer_id = inner_2.id
It does not print out the company (Allied) without a customer or total. So close, I just need a nudge in the right direction. Thanks.
Since the orders are linked to the companies via the customers, I don't think you need to perform two separate subqueries and join them; rather, I think you can just write:
SELECT companies.company AS "Company",
IFNULL(COUNT(DISTINCT companies_customers.customer_id), 0) AS "Total Customers",
IFNULL(SUM(orders.amount), 0) AS "All Orders Total"
FROM companies
LEFT
JOIN companies_customers
ON companies_customers.company_id = companies.id
LEFT
JOIN orders
ON orders.customer_id = companies_customers.customer_id
GROUP
BY companies.id
;
Edited to add: That said, I have to say that the schema doesn't really make sense to me. You have a many-to-many relationship between customers and companies — so, for example, John Smith is a customer of Acme Widget Company and of Intrepid Inc. — but then orders are just a property of the customer, not of the company. This means that if an order belongs to John Smith, then it necessarily belongs both to Acme Widget Company and to Intrepid Inc.. I don't think that can be right. Instead of having a customer_id field, I think orders needs to have a companies_customers_id field.
I have 3 table that to keep team,tournament_round AND score_team_member about competition TEAM_A Vs TEAM_B ,multiple of round.(1,2,3...n) and multiple of members of team there is score by oneself.This code above are useful very much.
SELECT team.name AS "TEAM",team.id,
IFNULL(COUNT(DISTINCT `tournament_round`.id), 0) AS "TotalWin",
IFNULL(SUM(`score_team_member`.`score`)/(select count(*) from `team_member`where team_id=team.id group by team_id ), 0) AS "ScoreofTeam"
FROM `team`
LEFT
JOIN `tournament_round`
ON `tournament_round`.team_winner_id = `team`.id
LEFT
JOIN `score_team_member`
ON `score_team_member`.team_id = `team`.id
WHERE `team`.thematch_id='6' AND `team`.`category1`='MEP'
GROUP BY `team`.id ORDER by `TotalWin`DESC ,`ScoreofTeam` DESC
Sample out put click here JPG
Table Designer JPG