Calcul (multiplication) of two select-result - sql

I'm trying to multiply two numbers I got from a SELECT statement of a unique query. I want to get the number of providers and the number of proposals (the query I made displays that), and multiply both on the same line (that I can't do).
I've made a very simple example to show you (same code as below) : DEMO ON FIDDLE
Create 2 providers working on 2 departments :
CREATE TABLE ##Provider
(
id INT,
p_name VARCHAR(50),
id_dep INT
)
INSERT INTO ##Provider (id, p_name, id_dep) VALUES
(1, 'toto', 10),
(2, 'toto', 11),
(3, 'tata', 9);
Create 4 proposal on 2 departments :
CREATE TABLE ##Proposal
(
id INT,
c_name VARCHAR(50),
id_dep INT
)
INSERT INTO ##Proposal (id, c_name, id_dep) VALUES
(1, 'propA', 10),
(2, 'propB', 09),
(3, 'propC', 10),
(4, 'propD', 10);
Create the department table :
CREATE TABLE ##Department
(
id INT,
d_name VARCHAR(50)
)
INSERT INTO ##Department (id, d_name) VALUES
(9, 'dep9')
,(10, 'dep10')
,(11, 'dep11');
Here I can display the number of providers and proposals by department (the real query is a lot more complex so I'd like to keep the 2 subrequests) :
select
id,
d_name,
nb_provider = (
SELECT COUNT(DISTINCT Id)
FROM ##Provider p
WHERE p.id_dep = dep.id
),
nb_proposal = (
SELECT COUNT(DISTINCT Id)
FROM ##Proposal pp
WHERE pp.id_dep = dep.id
)
from ##Department dep
WHERE dep.id = 10
But I CAN'T display a calcul of those two number :
select
id,
d_name,
nb_provider = (
SELECT COUNT(DISTINCT Id)
FROM ##Provider p
WHERE p.id_dep = dep.id
),
nb_proposal = (
SELECT COUNT(DISTINCT Id)
FROM ##Proposal pp
WHERE pp.id_dep = dep.id
),
calcul = (nb_provider * nb_proposal) --> DOESN'T WORK
from ##Department dep
WHERE dep.id = 10
I haven't tried a lot because I am not sure if this is even possible... maybe should I use UNION ?

I would recommend lateral joins:
select
d.id,
d.d_name,
p.nb_provider,
pp.nb_proposal
(p.nb_provider * pp.nb_proposal) calcul
from ##department d
outer apply (
select count(distinct id) nb_provider
from ##provider p
where p.id_dep = d.id
) p
outer apply (
select count(distinct id) nb_proposal
from ##proposal pp
where pp.id_dep = d.id
) pp
where d.id = 10

Related

PostgreSQL merge recursive query and JOIN

I have the following schema:
CREATE TABLE tbl_employee_team
(
employee_id int,
teams_id int
);
INSERT INTO tbl_employee_team
VALUES
(1, 2),
(1, 3),
(1, 4);
CREATE TABLE tbl_team_list_serv
(
service_id int,
team_id int
);
INSERT INTO tbl_team_list_serv
VALUES
(7, 2),
(9, 3),
(10, 4);
CREATE TABLE tbl_service
(
id int,
parent int
);
INSERT INTO tbl_service
VALUES
(5, null),
(6, 5),
(7, 6),
(8, null),
(9, 8),
(10, null);
For the sake of simplicity I declared:
1 as employee_id
2, 3, 4 as team_id
5 -> 6 -> 7 as service (5 is the main service)
8 -> 9 (8 is the main service)
10 (10 is the main service)
To retrieve the services the employee belongs to I query
SELECT ls.service_id FROM tbl_team_list_serv ls
JOIN tbl_employee_team t ON ls.team_id=t.teams_id WHERE t.employee_id = 1
To get the main service from the services I use
WITH RECURSIVE r AS
(
SELECT id, parent, 1 AS level
FROM tbl_service
WHERE id = 7 /*(here's I need to assign to every id from the JOIN)*/
UNION
SELECT tbl_service.id, tbl_service.parent, r.level + 1 AS level
FROM tbl_service
JOIN r
ON r.parent = tbl_service.id
)
SELECT id FROM r WHERE r.level = (SELECT max(level) FROM r)
My question is how do I merge the two queries?
Based on the data above I want to finally get a list of ids which is in this case:
5, 8, 10
Also, I want my recursive query to return the last row (I don't think that the solution with level is elegant)
SQLFiddle can be found here
Thanks in advance
I feel like you already did most of the work for this question. This is just a matter of the following tweaks:
Putting the logic for the first query in the anchor part of the CTE.
Adding the original service id as a column to remember the hierarchy.
Tweaking the final logic to get one row per original service.
As a query:
WITH RECURSIVE r AS (
SELECT ls.service_id as id, s.parent, 1 as level, ls.service_id as orig_service_id
FROM tbl_team_list_serv ls JOIN
tbl_employee_team t
ON ls.team_id = t.teams_id JOIN
tbl_service s
ON ls.service_id = s.id
WHERE t.employee_id = 1
UNION ALL
SELECT s.id, s.parent, r.level + 1 AS level, r.orig_service_id
FROM tbl_service s JOIN
r
ON r.parent = s.id
)
SELECT r.id
FROM (SELECT r.*,
MAX(level) OVER (PARTITION BY orig_service_id) as max_level
FROM r
) r
WHERE r.level = max_level;
Here is a db<>fiddle.

What is the correct order for recursive queries using two tables on SQL?

I have the following two tables:
CREATE TABLE empleados (
id INTEGER PRIMARY KEY,
nombre VARCHAR(255) NOT NULL,
gerenteId INTEGER,
FOREIGN KEY (gerenteId) REFERENCES empleados(id)
);
CREATE TABLE ventas (
id INTEGER PRIMARY KEY,
empleadoId INTEGER NOT NULL,
valorOrden INTEGER NOT NULL,
FOREIGN KEY (empleadoId) REFERENCES empleados(id)
);
With the following data:
INSERT INTO empleados(id, nombre, gerenteId) VALUES(1, 'Roberto', null);
INSERT INTO empleados(id, nombre, gerenteId) VALUES(2, 'Tomas', null);
INSERT INTO empleados(id, nombre, gerenteId) VALUES(3, 'Rogelio', 1);
INSERT INTO empleados(id, nombre, gerenteId) VALUES(4, 'Victor', 3);
INSERT INTO empleados(id, nombre, gerenteId) VALUES(5, 'Johnatan', 4);
INSERT INTO empleados(id, nombre, gerenteId) VALUES(6, 'Gustavo', 2);
INSERT INTO ventas(id, empleadoId, valorOrden) VALUES(1, 3, 400);
INSERT INTO ventas(id, empleadoId, valorOrden) VALUES(2, 4, 3000);
INSERT INTO ventas(id, empleadoId, valorOrden) VALUES(3, 5, 3500);
INSERT INTO ventas(id, empleadoId, valorOrden) VALUES(4, 2, 40000);
INSERT INTO ventas(id, empleadoId, valorOrden) VALUES(5, 6, 3000);
I'm trying to get a query to obtain the sum of all the "Orders" which belong directly or inderectly to the main managers. The main managers are the ones whose doesn't report to anybody else. In this calse, Roberto and Tomas are the main managers but there could be other ones. The result must to take into account not just the sales (ventas) made directly by him but also by any of their employees (direct employees or employees of their employees).
So in this case I'm expecting the following result:
-- Id TotalVentas
-- ----------------
-- 1 6900
-- 2 43000
Where the Id column refers to the employees' id which are "main" managers and TotalVentas column is the sum of all the ventas (valorOrden) made by them and their employees.
So Roberto has no records for orders but Rogelio (his employee) has one of 400, Victor (Rogelio's employee) has one for 3000 and Johnatan (Victor's employee) has another for 3500. So the sum of all of them is 6900. And it is the same case with Tomas which has one venta directly made by him plus another one made by Gustavo who is his employee.
The query that I have so far is the following:
WITH cte_org AS (
SELECT
id,
nombre,
gerenteId,
0 as EmpLevel
FROM
dbo.empleados
WHERE gerenteId IS NULL
UNION ALL
SELECT
e.id,
e.nombre,
e.gerenteId,
o.EmpLevel + 1
FROM
dbo.empleados e
INNER JOIN cte_org o
ON o.id = e.gerenteId
WHERE e.gerenteId IS NOT NULL
)
SELECT cte.id, SUM(s.orderValue)
FROM cte_org cte, dbo.sales s
WHERE (cte.id = s.employeeId AND cte.gerenteId is null)
OR
(cte.id = s.employeeId AND cte.EmpLevel <> 0 AND
cte.gerenteId in (select ee.id from dbo.empleados ee where ee.gerenteId is null)
)
--AND
--(cte.gerenteId in (select ee.id from dbo.empleados ee where ee.gerenteId is null)
--OR
--cte.gerenteId is null)
--AND cte.gerenteId = NULL
group by cte.id
;
Could anybody help me with this?
This is traversing a hierarchy, starting with the highest level managers, and then joining in the sales:
with cte as (
select id, nombre, id as manager
from empleados e
where gerenteid is null
union all
select e.id, e.nombre, cte.manager
from cte join
empleados e
on cte.id = e.gerenteid
)
select cte.manager, sum(valororden)
from cte join
ventas v
on cte.id = v.empleadoid
group by cte.manager;
Here is a db<>fiddle. The Fiddle uses SQL Server, because that is consistent with the syntax that you are using.
In oracle you can do this using below query:
SQLFiddle
select manager, sum(amount) as total_amount from
(
select level, CONNECT_BY_ROOT employeeid Manager, a.* from
(
SELECT a.id as employeeid, a.nombre as name , a.GERENTEID as manager_id,
b.EMPLEADOID as sales_id, b.VALORORDEN amount
from empleados a left outer join ventas b
on (a.id = b.empleadoId)
) a
start with manager_id is null
connect by prior employeeid = manager_id) x
group by manager;

A Better Way for Conditional Counting?

Imagine I have a CTE that creates a result set containing all of the information I need and need to do a bunch of conditional counting on the result set. Is there a better way to do it than a bunch of subqueries?
I can't use count() over () either as I need to sometimes do a distinct count on values and using a case when val=true then 1 else null end to conditionally count doesn't let me distinctly count, not to mention that it is basically the same as doing a bunch of subqueries.
Any recommendations, or is creating a bunch of subqueries the way to go?
(example SQL Fiddle)
Table Definitions
create table person (id int, name varchar2(20), age int, cityID int);
create table city (id int, name varchar2(20), stateID int);
create table state (id int, name varchar2(20));
insert into person values(1, 'Bob', 45, 1);
insert into person values(2, 'Joe', 33, 1);
insert into person values(3, 'Craig', 20, 1);
insert into person values(4, 'Alex', 45, 2);
insert into person values(5, 'Kevin', 33, 3);
insert into city values(1, 'Chicago', 1);
insert into city values(2, 'New York', 2);
insert into city values(3, 'Los Angeles', 3);
insert into state values(1, 'Illinois');
insert into state values(2, 'New York');
insert into state values(3, 'California');
SQL Query Example
with cte as (
select p.name pName
, p.age pAge
, c.name cName
, s.name sName
from person p
inner join city c
on p.cityID = c.ID
inner join state s
on c.stateID = s.ID
)
select distinct
(select count(*) from cte) totalRows
, (select count(*) from cte where pAge = 45) total45YO
, (select count(*) from cte where cName like 'Chicago') totalChicago
, (select count(distinct cName) from cte) totalCities
from cte
An example output I would hope for
TOTALROWS TOTAL45YO TOTALCHICAGO TOTALCITIES
------------------------------------------------------
5 2 3 3
Easiest is just as #jarlh mentions and use case/sum combinations to accomplish as follows.
SQL> select count(*) totalRows
2 , sum(case when p.age=45 then 1 else 0 end) total45YO
3 , sum(case when c.name like 'Chicago' then 1 else 0 end) totalChicago
4 , count(distinct c.name) totalCities
5 from person p
6 inner join city c
7 on p.cityID = c.ID
8 inner join state s
9 on c.stateID = s.ID;
TOTALROWS TOTAL45YO TOTALCHICAGO TOTALCITIES
____________ ____________ _______________ ______________
5 2 3 3
SQL>

Number of records created per day

In my PostgreSQL database I have the following schema:
CREATE TABLE programs (
id integer,
description text
);
CREATE TABLE public.messages (
id integer,
program_id integer,
text text,
message_template_id integer
);
CREATE TABLE public.message_templates (
id integer,
deliver_day integer
);
INSERT INTO programs VALUES(1, 'Test program');
INSERT INTO messages VALUES(1,1, 'Test message 1', 1);
INSERT INTO message_templates VALUES(1, 1);
INSERT INTO messages VALUES(2,1, 'Test message 2', 2);
INSERT INTO message_templates VALUES(2, 3);
INSERT INTO messages VALUES(3,1, 'Test message 3', 3);
INSERT INTO message_templates VALUES(3, 5);
Now I want to get number of message sent per day throughout the life of the program, query result should look like this:
day count
--------|----------
1 1
2 0
3 1
4 0
5 1
Is there any way of doing that in PostgreSQL?
https://www.db-fiddle.com/f/gvxijmp8u6wr6mYcSoAeVV/2
I decided to use generate_series:
SELECT d AS "Day", count(mt.id) FROM generate_series(
(SELECT min(delivery_day) from message_templates),
(SELECT max(delivery_day) from message_templates)
) d
left join message_templates mt on mt.delivery_day = d
group by d.d
Query is working fine. Maybe there is better way of doing this?
You could use this:
WITH tmp AS
(
SELECT m.program_id, a.n AS d
FROM generate_series(1,
(SELECT MAX(deliver_day) FROM message_templates)
) AS a(n)
CROSS JOIN
(
SELECT DISTINCT program_id
FROM messages
) m
)
SELECT t.program_id,
t.d AS "day",
COUNT(m.program_id) AS "count" -- COUNT(m.id)
FROM tmp t
LEFT JOIN message_templates mt
ON t.d = mt.deliver_day
LEFT JOIN messages m
ON m.message_template_id = mt.id AND t.program_id = m.program_id
GROUP BY t.program_id, t.d
ORDER BY t.program_id, t.d;
Tested in db-fiddle

query to count number of unique relations

I have 3 tables:
t_user (id, name)
t_user_deal (id, user_id, deal_id)
t_deal (id, title)
multiple user can be linked to the same deal. (I'm using oracle but it should be similar, I can adapt it)
How can I get all the users (name) with the number of unique user he made a deal with.
let's explain with some data:
t_user:
id, name
1, joe
2, mike
3, John
t_deal:
id, title
1, deal number 1
2, deal number 2
t_user_deal:
id, user_id, deal_id
1, 1, 1
2, 2, 1
3, 1, 2
4, 3, 2
the result I expect:
user_name, number of unique user he made a deal with
Joe, 2
Mike, 1
John, 1
I've try this but I didn't get the expected result:
SELECT tu.name,
count(tu.id) AS nbRelations
FROM t_user tu
INNER JOIN t_user_deal tud ON tu.id = tud.user_id
INNER JOIN t_deal td ON tud.deal_id = td.id
WHERE
(
td.id IN
(
SELECT DISTINCT td.id
FROM t_user_deal tud2
INNER JOIN t_deal td2 ON tud2.deal_id = td2.id
WHERE tud.id <> tud2.user_id
)
)
GROUP BY tu.id
ORDER BY nbRelations DESC
thanks for your help
This should get you the result
SELECT id1, count(id2),name
FROM (
SELECT distinct tud1.user_id id1 , tud2.user_id id2
FROM t_user_deal tud1, t_user_deal tud2
WHERE tud1.deal_id = tud2.deal_id
and tud1.user_id <> tud2.user_id) as tab, t_user tu
WHERE tu.id = id1
GROUP BY id1,name
Something like
select name, NVL (i.ud, 0) ud from t_user join (
SELECT user_id, count(*) ud from t_user_deal group by user_id) i on on t_user.id = i.user_id
where i.ud > 0
Unless I'm missing somethig here. It actually sounds like your question references having a second user in the t_user_deal table. The model you've described here doesn't include that.
PostgreSQL example:
create table t_user (id int, name varchar(255)) ;
create table t_deal (id int, title varchar(255)) ;
create table t_user_deal (id int, user_id int, deal_id int) ;
insert into t_user values (1, 'joe'), (2, 'mike'), (3, 'john') ;
insert into t_deal values (1, 'deal 1'), (2, 'deal 2') ;
insert into t_user_deal values (1, 1, 1), (2, 2, 1), (3, 1, 2), (4, 3, 2) ;
And the query.....
SELECT
name, COUNT(DISTINCT deal_id)
FROM
t_user INNER JOIN t_user_deal ON (t_user.id = t_user_deal.user_id)
GROUP BY
user_id, name ;
The DISTINCT might not be necessary (in the COUNT(), that is). Depends on how clean your data is (e.g., no duplicate rows!)
Here's the result in PostgreSQL:
name | count
------+-------
joe | 2
mike | 1
john | 1
(3 rows)