Select persons who do only the project with the same city - tsql

So I have 3 tables: Management (…, personID, projectID //personID and projectID can dublicate) Project(id, city, …) and Person (id, name, city, …)
I need to output persons who only do projects with the same city.
Will be greatfull for any help or idea.

Here's a simple query with joins, group by, having, and count(distinct) that gets the desired results.
First, create and populate sample tables (Please save us this step in your future questions):
DECLARE #Project as table
(
id int,
city varchar(20)
);
DECLARE #Person as table
(
id int,
firstName varchar(10),
lastName varchar(10)
);
DECLARE #Managment as table
(
projectId int,
personId int
);
INSERT INTO #Project (id, city) VALUES
(1, 'Abilene'),
(2, 'Akron'),
(3, 'Albuquerque'),
(4, 'Alexandria'),
(5, 'Allentown');
INSERT INTO #Person (Id, firstName, lastName) VALUES
(1, 'Aaron', 'Carter'),
(2, 'Aaron', 'Eckhart'),
(3, 'Abbie', 'Cornish'),
(4, 'Ace', 'Young'),
(5, 'Adam', 'Brody');
INSERT INTO #Managment (projectId, personId) VALUES
(1, 1),
(1, 2),
(3, 3),
(3, 2),
(3, 5),
(4, 4),
(4, 1),
(5, 5);
The query:
SELECT personId, firstName, lastName, max(city) as city
FROM #Person As pe
JOIN #Managment As m
ON pe.Id = m.personId
JOIN #Project As pr
ON pr.Id = m.projectId
GROUP BY personId, firstName, lastName
HAVING COUNT(DISTINCT city) = 1
(I've added the city's name as a bonus if you wanted two cities you could go with max and min, more than that you can't) - note the usage of count(distinct city) in the having clause.
Results:
personId firstName lastName city
3 Abbie Cornish Albuquerque
4 Ace Young Alexandria

Related

PostgreSQL: Forward fill NULL values with previous NOT NULL value in group

I'm trying fill NULL values in multiple columns (different column types INT, VARCHAR) with previous NOT NULL value in a group ordered by date. Considering following table:
I want to get here:
CREATE TABLE IF NOT EXISTS test (
id VARCHAR,
date DATE,
value_1 INT,
value_2 VARCHAR
);
INSERT INTO test VALUES
(1, '2022-01-01', 5, 'asdf'),
(1, '2022-01-02', NULL, NULL),
(1, '2022-01-03', NULL, 'def'),
(1, '2022-01-04', 4, NULL),
(2, '2022-01-01', 1, 'a'),
(2, '2022-01-02', NULL, NULL),
(2, '2022-01-03', 2, 'b'),
(2, '2022-01-04', NULL, NULL);
One day, PostgreSQL may support the IGNORE NULLS option for LEAD and LAG functions.
In the mean time, you must use window functions to build groups, then select the maximum in each group.
SELECT id, date,
MAX(value_1) OVER (PARTITION BY id, grp_1) AS value_1,
MAX(value_2) OVER (PARTITION BY id, grp_2) AS value_2
FROM(
SELECT *,
COUNT(value_1) OVER (PARTITION BY id ORDER BY Date DESC) as grp_1,
COUNT(value_2) OVER (PARTITION BY id ORDER BY Date DESC) as grp_2
FROM test
) T
ORDER BY ID, date

How to filter out updated records from CTE in postgres?

I am trying to perform an update and insert operation on a partitioned table using the same CTE. The update query works perfectly fine. But during insert, I just want those records from CTE which were not updated so that I can make an optimal insert with least cost.
Below is my table and query :
create table scientist (id integer, firstname varchar(100), lastname varchar(100));
insert into scientist (id, firstname, lastname) values (1, 'albert', 'einstein');
insert into scientist (id, firstname, lastname) values (2, 'isaac', 'newton');
insert into scientist (id, firstname, lastname) values (3, 'marie', 'curie');
select * from scientist;
alter table scientist add constraint id_pk PRIMARY KEY ("id");
with temp(id, firstname, lastname) as (
Select *
from (values(1, 'albert','besra'),
(5, 'abc', 'def')
) as a(id, firstname, lastname)
),
updated as(
update scientist s
set lastname = t.lastname
from temp t
where s.id = t.id returning t.*
)
Select *
from updated;
At present, it is returning all the rows. I was able to filter out records which were updated but that increased the total cost of my operation.
where not exists (Select 1 from updated where updated.id = temp.id )
Is there a way I can do that ?

Postgresql find by count, joined table

Given 3 tables. I need to build SQL query to find two actors who CAST TOGETHER THE MOST and list the titles of those movies. Sort alphabetically
https://www.db-fiddle.com/f/r2Y9CpH8n7MHTeBaqEHe9S/0
The data for reproducing below:
create table film_actor
(
actor_id integer,
film_id integer
)
;
create table film
(
film_id integer,
title varchar
)
;
create table actor
(
actor_id integer,
first_name varchar,
last_name varchar
)
;
INSERT INTO public.film_actor (actor_id, film_id) VALUES (1, 1);
INSERT INTO public.film_actor (actor_id, film_id) VALUES (1, 2);
INSERT INTO public.film_actor (actor_id, film_id) VALUES (1, 3);
INSERT INTO public.film_actor (actor_id, film_id) VALUES (2, 1);
INSERT INTO public.film_actor (actor_id, film_id) VALUES (2, 2);
INSERT INTO public.film_actor (actor_id, film_id) VALUES (2, 3);
INSERT INTO public.film_actor (actor_id, film_id) VALUES (3, 1);
INSERT INTO public.film (film_id, title) VALUES (1, 'First');
INSERT INTO public.film (film_id, title) VALUES (2, 'Second');
INSERT INTO public.film (film_id, title) VALUES (3, 'Third');
INSERT INTO public.film (film_id, title) VALUES (4, 'Fourth');
INSERT INTO public.actor (actor_id, first_name, last_name) VALUES (1, 'John', 'Snow');
INSERT INTO public.actor (actor_id, first_name, last_name) VALUES (2, 'Spider', 'Man');
INSERT INTO public.actor (actor_id, first_name, last_name) VALUES (3, 'Mike', 'Kameron');
Is this what you are looking for?
with acting_pairs as (
select a1.actor_id as a1_id, a2.actor_id as a2_id
from film_actor a1
join film_actor a2 on a1.film_id = a2.film_id
where a1.actor_id < a2.actor_id
)
select a1_id, a2_id, count(*) as total
from acting_pairs
group by (a1_id, a2_id)
order by total desc
limit 1
Giving us expected output for the example input would be nice.

Aggregation giving wrong result in T-SQL

I am getting wrong sum of values in more than two tables.
Please advise the correct qry for the below
my sample data is as follows.
IF OBJECT_ID('tempdb..#Departamentos') IS NOT NULL
DROP TABLE #Departamentos
GO
CREATE TABLE #Departamentos (ID INT IDENTITY(1,1) PRIMARY KEY,
Nome_Dep VARCHAR(200))
GO
INSERT INTO #Departamentos(Nome_Dep)
VALUES('Vendas'), ('TI'), ('Recursos Humanos')
GO
IF OBJECT_ID('tempdb..#Funcionarios') IS NOT NULL
DROP TABLE #Funcionarios
GO
CREATE TABLE #Funcionarios (ID INT IDENTITY(1,1) PRIMARY KEY,
ID_Dep INT,
Nome VARCHAR(200),
Salario Numeric(18,2))
GO
INSERT INTO #Funcionarios (ID_Dep, Nome, Salario)
VALUES(1, 'Fabiano', 2000), (1, 'Amorim', 2000), (1, 'Diego', 9000),
(2, 'Felipe', 2000), (2, 'Ferreira', 2500), (2, 'Nogare', 11999),
(3, 'Laerte', 5000), (3, 'Luciano', 23500), (3, 'Zavaschi', 13999)
GO
IF OBJECT_ID('tempdb..#deals') IS NOT NULL
DROP TABLE #deals
GO
CREATE TABLE #deals (ID INT IDENTITY(1,1) PRIMARY KEY,
ID_Deal INT,
Nome VARCHAR(200),
Revenue Numeric(18,2))
GO
INSERT INTO #deals (ID_Deal, Nome, Revenue)
VALUES(1, 'Fabiano', 50), (1, 'Amorim', 20), (1, 'Diego', 90),
(2, 'Felipe', 20), (2, 'Ferreira', 25), (2, 'Nogare', 119),
(3, 'Laerte', 50), (3, 'Luciano', 23), (3, 'Zavaschi', 13)
GO
My query is as follows to get the result:
select d.id,d.Nome_Dep,sum(salario)salario,sum(revenue)revenue from #Departamentos d left join #Funcionarios f on d.ID=f.ID_Dep
left join #deals dd on dd.ID_Deal=d.ID
group by d.id,d.Nome_Dep
But my desired result should be:
ID Nome_Dep salario revenue
1 Vendas 13000.00 160.00
2 TI 16499.00 164.00
3 Recursos Humanos 42499.00 86.00
Thanks
There is probably duplication happening because of the double table join. One way around this is to aggregate each table in separate subqueries, and then join to them:
SELECT
d.id,
d.Nome_Dep,
COALESCE(f.sum_salario, 0) AS sum_salario,
COALESCE(dd.sum_revenue, 0) AS sum_revenue
FROM #Departamentos d
LEFT JOIN
(
SELECT ID_Dep, SUM(salario) AS sum_salario
FROM #Funcionarios
GROUP BY ID_Dep
) f
ON d.ID = f.ID_Dep
LEFT JOIN
(
SELECT ID_Deal, SUM(revenue) AS sum_revenue
FROM #deals
GROUP BY ID_Deal
) dd
ON d.ID = dd.ID_Deal;
Demo

Transpose records from 2 "one to many" tables into one record

Basically, I would like to get a recordset similar to this:
CustomerID, CustomerName, OrderNumbers
1 John Smith 112, 113, 114, 115
2 James Smith 116, 117, 118
Currently I am using an Sql Server UDF to concatenate order #s.
Are there more efficient solutions ?
1) There are allot of solutions.
2) Example (SQL2005+):
DECLARE #Customer TABLE (
CustomerID INT PRIMARY KEY,
Name NVARCHAR(50) NOT NULL
);
INSERT #Customer VALUES (1, 'Microsoft');
INSERT #Customer VALUES (2, 'Macrosoft');
INSERT #Customer VALUES (3, 'Appl3');
DECLARE #Order TABLE (
OrderID INT PRIMARY KEY,
CustomerID INT NOT NULL -- FK
);
INSERT #Order VALUES (1, 1);
INSERT #Order VALUES (2, 2);
INSERT #Order VALUES (3, 2);
INSERT #Order VALUES (4, 1);
INSERT #Order VALUES (5, 2);
SELECT c.CustomerID, c.Name, oa.OrderNumbers
FROM #Customer AS c
/*CROSS or */OUTER APPLY (
SELECT STUFF((
SELECT ',' + CONVERT(VARCHAR(11), o.OrderID) FROM #Order AS o
WHERE o.CustomerID = c.CustomerID
-- ORDER BY o.OrderID -- Uncomment of order nums must be sorted
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)'), 1, 1, '') AS OrderNumbers
) oa;
Output:
CustomerID Name OrderNumbers
----------- --------- ------------
1 Microsoft 1,4
2 Macrosoft 2,3,5
3 Appl3 NULL