2

I have a little question: I have a view and want to make a small recursive (my approach) call to a table called employee. There are the employees and a column "reportsTo", which gives the EmployeeId of the respective superior or parent.

There is also an invoice table with all purchases made by customers. The customers have a direct contact person / seller (SupportRepId). Now the annual sales of the employees are to be listed, as well as those of the direct employees. (Reverse ReportTo!)

create view employee_sales_v as 
   select employee.LastName, employee.ReportsTo, sum(invoice.total) sales, 
   year(invoice.InvoiceDate) year 
from employee 
join customer on customer.SupportRepId = employee.EmployeeId 
join invoice on invoice.CustomerId = customer.CustomerId
group by employee.LastName, year(invoice.InvoiceDate);

How can I get these sales?

SampleData:

CREATE TABLE `Customer`
(
    `CustomerId` INT NOT NULL AUTO_INCREMENT,
    `SupportRepId` INT,
    CONSTRAINT `PK_Customer` PRIMARY KEY  (`CustomerId`)
);

CREATE TABLE `Employee`
(
    `EmployeeId` INT NOT NULL AUTO_INCREMENT,
    `LastName` NVARCHAR(20) NOT NULL,
    `FirstName` NVARCHAR(20) NOT NULL,
    `ReportsTo` INT,
    CONSTRAINT `PK_Employee` PRIMARY KEY  (`EmployeeId`)
);

CREATE TABLE `Invoice`
(
    `InvoiceId` INT NOT NULL AUTO_INCREMENT,
    `CustomerId` INT NOT NULL,
    `InvoiceDate` DATETIME NOT NULL,
    `Total` NUMERIC(10,2) NOT NULL,
    CONSTRAINT `PK_Invoice` PRIMARY KEY  (`InvoiceId`)
);

INSERT INTO `Employee` (`LastName`, `FirstName`, `ReportsTo`) VALUES (N'HGF', N'POI', 0);
INSERT INTO `Employee` (`LastName`, `FirstName`, `ReportsTo`) VALUES (N'XYZ', N'ABC', 1);

INSERT INTO `Customer` (`SupportRepId`) VALUES (2);
INSERT INTO `Customer` (`SupportRepId`) VALUES (2);

INSERT INTO `Invoice` (`CustomerId`, `InvoiceDate`, `Total`) VALUES (1, '2013/1/1', 1.98);
INSERT INTO `Invoice` (`CustomerId`, `InvoiceDate`, `Total`) VALUES (2, '2009/10/2', 3.96);
INSERT INTO `Invoice` (`CustomerId`, `InvoiceDate`, `Total`) VALUES (2, '2010/5/3', 5.94);
Thom A
  • 88,727
  • 11
  • 45
  • 75
L30N
  • 23
  • 3
  • 2
    Please only tag the RDBMS you are really using. This is clearly MySQL not T-SQL, so I have removed the tag [tag:sql-server]. – Thom A Dec 16 '20 at 17:36
  • Also report the version of MySQL you use. Run the query `SELECT @@version;` and tell us the result. The answer to your question depends on the version of MySQL. – Bill Karwin Dec 16 '20 at 18:00
  • Thank you for the comments! I'm using version 8.0.22. – L30N Dec 16 '20 at 18:38
  • Why do you have a VIEW? – Strawberry Dec 16 '20 at 18:48
  • Yes, the view is actually unimportant for the solution, but I made it for it anyway. (I'm still very new to database programming :)) – L30N Dec 17 '20 at 09:18

1 Answers1

0

... as well as those of the direct employees.

Since only the sales total of the direct employees are needed, recursion is not required in this case.

SQL:

WITH CTE AS 
    (SELECT SupportRepId, year(i.InvoiceDate) AS YEAR, sum(i.total) total
    FROM Customer c 
    INNER JOIN Invoice i on i.CustomerId = c.CustomerId
    GROUP BY year(i.InvoiceDate), SupportRepId) ---CREATE CTE
SELECT 
    e1.LastName, 
    e1.ReportsTo, 
    t1.year,
    IFNULL(cte1.total,0.00) AS `My Sales`,
    IFNULL(t2.total,0.00) AS `Reportees Sales`
FROM Employee e1 
INNER JOIN (SELECT DISTINCT year(i.InvoiceDate) AS year from Invoice i) t1 on 1 = 1 --- ALL DISTINCT YEARS
LEFT JOIN CTE cte1 on e1.EmployeeId = cte1.SupportRepId and t1.year = cte1.year
LEFT JOIN 
    (SELECT 
     e2.ReportsTo, cte2.year, sum(cte2.total) total 
     FROM Employee e2
     INNER JOIN CTE cte2 ON e2.EmployeeId = cte2.SupportRepId
    GROUP BY e2.ReportsTo, cte2.year) t2  --- GET EMPLOYEE DETAILS
    ON t2.ReportsTo = e1.EmployeeId AND t1.year = t2.year
--- WHERE cte1.year is not null || t2.year is not null
ORDER by e1.LastName, t1.year

Details:

  • --- CREATE CTE : Fetch employees and their sales for each year. Created this subquery as cte since it needs to be reused
  • --- ALL DISTINCT : Fetch all the distinct year the invoice is generated. This will allow us to generate report for employees even if they have not made any sales on a year. (If that is not required, uncomment WHERE cte1.year is not null || t2.year is not null to filter out employees and reportes with no sales for a year).
  • --- GET EMPLOYEE DETAILS : This subquery fetches the sales details of the reportees.

Demo:

Fiddle Link over here. I have added additional test cases for validation.


Update: Query update for pre MySQL 8

SELECT 
    e1.LastName, 
    e1.ReportsTo, 
    t1.year,
    IFNULL(cte1.total,0.00) AS `My Sales`,
    IFNULL(t2.total,0.00) AS `Reportees Sales`
FROM Employee e1 
INNER JOIN (SELECT DISTINCT year(i.InvoiceDate) AS year from Invoice i) t1 on 1 = 1 --- ALL DISTINCT YEARS
LEFT JOIN 
(SELECT SupportRepId, year(i.InvoiceDate) AS YEAR, sum(i.total) total
    FROM Customer c 
    INNER JOIN Invoice i on i.CustomerId = c.CustomerId
    GROUP BY year(i.InvoiceDate), SupportRepId) cte1 on e1.EmployeeId = cte1.SupportRepId and t1.year = cte1.year
LEFT JOIN 
    (SELECT 
     e2.ReportsTo, cte2.year, sum(cte2.total) total 
     FROM Employee e2
     INNER JOIN ((SELECT SupportRepId, year(i.InvoiceDate) AS YEAR, sum(i.total) total
                  FROM Customer c 
                  INNER JOIN Invoice i on i.CustomerId = c.CustomerId
                  GROUP BY year(i.InvoiceDate), SupportRepId)) cte2 
     ON e2.EmployeeId = cte2.SupportRepId
    GROUP BY e2.ReportsTo, cte2.year) t2  --- GET EMPLOYEE DETAILS
    ON t2.ReportsTo = e1.EmployeeId AND t1.year = t2.year
--- WHERE cte1.year is not null || t2.year is not null
ORDER by e1.LastName, t1.year

Fiddle link: https://www.db-fiddle.com/f/td8RaWwYr7AKp1wWGfJ8yK/3

Prasanna
  • 2,390
  • 10
  • 11
  • First of all, I would like to thank you very much for this detailed answer and the associated efforts. Apparently there is no CTE / With in our MySQL version. (See link: [here](https://stackoverflow.com/questions/1382573/how-do-you-use-the-with-clause-in-mysql#:~:text=MySQL%20prior%20to%20version%208.0,clause%20represents%20%2D%20they%20are%20interchangeable) ) PS: I'm still very new to database programming – L30N Dec 17 '20 at 09:15
  • I have tried the code in the MySQL Workbench in the version mentioned, but there is an error with the WITH, as can be seen in the [image](https://imgur.com/w8prbYH) – L30N Dec 17 '20 at 09:23
  • @user14837915, Ok. I have updated the pre mysql 8 version. without CTE. Can you try that? – Prasanna Dec 17 '20 at 09:28