Select row with max value with having clause - sql-server

create table Users
(
ID int primary key,
Username char(13) not null,
Salary int,
DepartmentID int,
PCID int
);
insert into Users values (1, 'Jenson', 180000, 4,12);
insert into Users values (2, 'John', 161000, 2,11);
insert into Users values (3, 'Jack', 150000, 1,10);
insert into Users values (4, 'James', 150000, 3,9);
insert into Users values (5, 'Jeremy', 151000, 3,7);
create table Departments
(
ID int primary key,
Name char(13) not null,
);
insert into Departments values (1, 'Programming');
insert into Departments values (2, 'Supply');
insert into Departments values (3, 'Medicine');
insert into Departments values (4, 'Economic');
insert into Departments values (5, 'Communication');
SELECT
s.dep_id as dep_id, s.Sum_Salary
FROM
(SELECT
d.ID AS dep_id, SUM(u.Salary) AS Sum_Salary
FROM
dbo.users u
INNER JOIN
Departments d ON u.DepartmentID = d.id
GROUP BY
d.ID) s
I can select from Department_id and sum_salary
How can I select row select row with max value of sum_salary? Not using CTE or same ways.

You can use TOP and ORDER BY for this:
SELECT TOP 1
d.ID AS dep_id,
sum(u.Salary) AS Sum_Salary
from dbo.users u
INNER JOIN Departments d ON u.DepartmentID=d.id
GROUP BY d.ID
order by Sum_Salary desc;
It'll return the top 1 row with maximum Sum_salary.
If you just want to find maximum sum_salary, use MAX:
SELECT
MAX(s.Sum_Salary)
FROM
(SELECT
SUM(u.Salary) AS Sum_Salary
FROM
dbo.users u
INNER JOIN
Departments d ON u.DepartmentID = d.id
GROUP BY
d.ID) s

WITH CTE AS
(
SELECT *,ROW_NUMBER() OVER( ORDER BY SUM_SALARY DESC) AS RN FROM (SELECT D.ID AS DEP_ID ,SUM(U.SALARY) AS SUM_SALARY FROM DBO.USERS U
INNER JOIN DEPARTMENTS D ON U.DEPARTMENTID=D.ID
GROUP BY D.ID )A
)
SELECT SUM_SALARY, RN
FROM CTE WHERE RN=1
OR
SELECT D.ID AS DEP_ID ,SUM(U.SALARY) AS SUM_SALARY FROM DBO.USERS U
INNER JOIN DEPARTMENTS D ON U.DEPARTMENTID=D.ID
GROUP BY D.ID
HAVING SUM(U.SALARY) = (SELECT TOP 1 SUM(U.SALARY) AS SUM_SALARY FROM DBO.USERS U
INNER JOIN DEPARTMENTS D ON U.DEPARTMENTID=D.ID
GROUP BY D.ID
ORDER BY SUM_SALARY DESC)

Related

CROSS APPLY in Oracle/SQL Server substitute in Snowflake

I am looking for the best alternatives on mapping Cross APPLY to SF.
Something like:
select department_name, employee_id, employee_name
from departments d
cross apply (select employee_id, employee_name
from employees e
where salary >= 2000
and e.department_id = d.department_id)
order by 1, 2, 3;
The ANSI SQL equivalent of CROSS APPLY is JOIN LATERAL:
select department_name, employee_id, employee_name
from departments d
join lateral (select employee_id, employee_name
from employees e
where salary >= 2000
and e.department_id = d.department_id)
order by 1, 2, 3;
Output:
and for OUTER APPLY is LEFT JOIN LATERAL () ON TRUE:
select department_name, employee_id, employee_name
from departments d
left join lateral (select employee_id, employee_name
from employees e
where salary >= 2000
and e.department_id = d.department_id) ON TRUE
order by 1, 2, 3;
Output:
For source data:
CREATE OR REPLACE TABLE departments(department_id INT, department_name TEXT,
deparment_location TEXT)
AS
SELECT 1, 'HR', 'London' UNION
SELECT 2, 'SALES', 'Berlin' UNION
SELECT 3, 'RESEARCH', 'Paris';
CREATE OR REPLACE TABLE employees(employee_id INT, employee_name TEXT,
salary INT, department_id INT)
AS
SELECT 100, 'John', 2000, 1 UNION
SELECT 101, 'Anna', 4000, 2;
Related: CROSS/OUTER APPLY in MySQL
What I have seen is that the same results can be achieved with a CROSS JOIN LATERAL
using this testdata from here:
create table departments (
department_id number(2) ,
department_name varchar2(14),
location varchar2(13)
);
insert into departments values (10,'ACCOUNTING','NEW YORK');
insert into departments values (20,'RESEARCH','DALLAS');
insert into departments values (30,'SALES','CHICAGO');
insert into departments values (40,'OPERATIONS','BOSTON');
create table employees (
employee_id number(4) ,
employee_name varchar2(10),
job varchar2(9),
manager_id number(4),
hiredate date,
salary number(7,2),
commission number(7,2),
department_id number(2)
);
insert into employees values (7369,'SMITH','CLERK',7902,to_date('17-12-1980','dd-mm-yyyy'),800,NULL,20);
insert into employees values (7499,'ALLEN','SALESMAN',7698,to_date('20-2-1981','dd-mm-yyyy'),1600,300,30);
insert into employees values (7521,'WARD','SALESMAN',7698,to_date('22-2-1981','dd-mm-yyyy'),1250,500,30);
insert into employees values (7566,'JONES','MANAGER',7839,to_date('2-4-1981','dd-mm-yyyy'),2975,NULL,20);
insert into employees values (7654,'MARTIN','SALESMAN',7698,to_date('28-9-1981','dd-mm-yyyy'),1250,1400,30);
insert into employees values (7698,'BLAKE','MANAGER',7839,to_date('1-5-1981','dd-mm-yyyy'),2850,NULL,30);
insert into employees values (7782,'CLARK','MANAGER',7839,to_date('9-6-1981','dd-mm-yyyy'),2450,NULL,10);
insert into employees values (7788,'SCOTT','ANALYST',7566,to_date('13-JUL-87','dd-mm-rr')-85,3000,NULL,20);
insert into employees values (7839,'KING','PRESIDENT',NULL,to_date('17-11-1981','dd-mm-yyyy'),5000,NULL,10);
insert into employees values (7844,'TURNER','SALESMAN',7698,to_date('8-9-1981','dd-mm-yyyy'),1500,0,30);
insert into employees values (7876,'ADAMS','CLERK',7788,to_date('13-6-87', 'dd-mm-yyyy')-51,1100,NULL,20);
insert into employees values (7900,'JAMES','CLERK',7698,to_date('3-12-1981','dd-mm-yyyy'),950,NULL,30);
insert into employees values (7902,'FORD','ANALYST',7566,to_date('3-12-1981','dd-mm-yyyy'),3000,NULL,20);
insert into employees values (7934,'MILLER','CLERK',7782,to_date('23-1-1982','dd-mm-yyyy'),1300,NULL,10);
As mentioned in the question, the following:
select department_name, employee_id, employee_name
from departments d
cross join lateral (select employee_id, employee_name
from employees e
where salary >= 2000
and e.department_id = d.department_id)
order by 1, 2, 3;
is equivalent, but is it the best option?

Sql find records in join table which match filter exactly

Best to explain with an example :)
Let's say I have a the tables
CREATE TABLE dbo.Customer (
CustomerId INT PRIMARY KEY,
Name NVARCHAR(50)
)
CREATE TABLE dbo.ShoppingBasket (
ShoppingBasketId INT PRIMARY KEY,
CustomerId INT NOT NULL FOREIGN KEY dbo.Customer(CustomerId),
ItemName NVARCHAR(50)
)
Example data
INSERT INTO dbo.Customer
VALUES (1, 'Steve'), (2, 'Bucky')
INSERT INTO dbo.ShoppingBasket
VALUES (1, 1, 'Banana'), (2, 1, 'Orange'), (3, 2, 'Orange')
Now, I want to find all customers, that have a Banana and an Orange in their shopping basket exactly. So in the case above, it should return Steve only. Since Bucky has only a Banana.
The following query works for this
SELECT *
FROM dbo.Customer AS c
WHERE EXISTS (
SELECT 1
FROM dbo.ShoppingBasket AS b
WHERE b.CustomerId = c.CustomerId
AND b.ItemName IN ('Banana', 'Orange')
GROUP BY CustomerId
HAVING COUNT(CustomerId) = 2
)
That's fine. Now, if I want all customers that only have an Orange, the above query fails since
SELECT *
FROM dbo.Customer AS c
WHERE EXISTS (
SELECT 1
FROM dbo.ShoppingBasket AS b
WHERE b.CustomerId = c.CustomerId
AND b.ItemName = 'Orange'
GROUP BY CustomerId
HAVING COUNT(CustomerId) = 1
)
is filtering out the shopping basket and then applying the group and having clause. Thus both Steve and Bucky are return whereas only Bucky should be returned.
Could someone point me in the right direction to find such a query, I suppose I can always do another NOT EXIST inside the exist subquery, to make sure no other items are found. E.g.
SELECT *
FROM dbo.Customer AS c
WHERE EXISTS (
SELECT 1
FROM dbo.ShoppingBasket AS b
WHERE b.CustomerId = c.CustomerId
AND b.ItemName = 'Orange'
AND NOT EXISTS (
SELECT 1
FROM dbo.ShoppingBasket AS b2
WHERE b2.CustomerId = b.CustomerId
AND b.ItemName <> 'Orange'
)
)
But was wandering if there's a more elegant way to handle it. One that preferably doesn't do an extra, negated join on the same table.
you should check the distinct ItemName instead of the customerId eg:
select c.*
from dbo.Customer
inner join(
select CustomerId, count(distinct ItemName) count_name
from ShoppingBasket
where ItemName IN ('Banana', 'Orange')
group by CustomerId
having count_name = 2
) t on t.CustomerId = c.CustomerId
If you need a twice check on number of item name an type you could compose the inner join in two part
select c.*
from dbo.Customer
inner join(
select CustomerId
from ShoppingBasket b
where ItemName IN ('Banana', 'Orange')
INNER JOIN (
Select CustomerId, count(distinct ItemName) count_name
from ShoppingBasket
group by CustomerId
having count_name = 2
) t2 ON t2.CustomerId = b.CustomerId
) t on t.CustomerId = c.CustomerId
and for Orange ..
select c.*
from dbo.Customer
inner join(
select CustomerId
from ShoppingBasket b
where ItemName IN ('Orange')
INNER JOIN (
Select CustomerId, count(distinct ItemName) count_name
from ShoppingBasket
group by CustomerId
having count_name = 1
) t2 ON t2.CustomerId = b.CustomerId
) t on t.CustomerId = c.CustomerId
The problem is that the in clause in ambiguos because return true also for ShoppingBasket CustomerId with One positive check
then instead of an in clause ( equivalent to OR ) you should work on and clause for all the customer that have a number of distinct name equivalent at then number you are looking for
Select CustomerId
from ShoppingBasket a
inner join ShoppingBasket b a.ItemName = 'Orange' and b.ItemName = 'Banana'
and customerId IN (
Select CustomerId
from ShoppingBasket
group by CustomerId
having count(distinct ItemName) = 2
)
I like to do this with group by and having. If you want both "banana" and "orange":
select sb.customerId
from dbo.ShoppingBasket sb
where sb.itemName in ('banana', 'orange')
group by sb.customerId
having count(distinct itemName) = 2; -- has both
If you want the two items and nothing else, then use this more general form:
select sb.customerId
from dbo.ShoppingBasket sb
where sb.itemName in ('banana', 'orange')
group by sb.customerId
having sum(case when sb.itemName = 'banana' then 1 else 0 end) > 0 and
sum(case when sb.itemName = 'orange' then 1 else 0 end) > 0 and
sum(case when sb.itemName not in ('orange, 'banana') then 1 else 0 end) = 0 ;
You can extend this version easily. Each item gets its own sum(). You can also include multiple items to support, say, "banana and (orange or clementine)".

How to join two Tables which have no unique ID

I am trying to Left join Table 1 to table 2
Table1 Table2
ID Data ID Data2
1 r 1 q
2 t 1 a
3 z 2 x
1 u 3 c
After i have left joined this two Tables i would like get something like this
Table1+2
ID Data Data2
1 r a
2 t x
3 z c
1 u q
and NOT
Table1+2
ID Data Data2
1 r q
2 t x
3 z c
1 u q
and my question is: is there any possibility to tell table 2 that if u have used something for table 1, dont use it and give me next value. do i have to make it im T-SQL or to and new column where i can list if this id exists then write 2 if not 1(Number data). How can i solve this problem?
Ty u in advance.
Since there's no uniqueness in the ID on both tables, lets add some uniqueness to it.
So it can be used to join on.
The window function ROW_NUMBER can be used for that.
An example solution that gives the expected result:
DECLARE #TestTable1 TABLE (ID INT, Data VARCHAR(1));
DECLARE #TestTable2 TABLE (ID INT, Data VARCHAR(1));
INSERT INTO #TestTable1 VALUES (1,'r'),(2,'t'),(3,'z'),(1,'u');
INSERT INTO #TestTable2 VALUES (1,'q'),(1,'a'),(2,'x'),(3,'c');
select
t1.ID, t1.Data,
t2.Data as Data2
from (
select ID, Data,
row_number() over (partition by ID order by Data) as rn
from #TestTable1
) t1
left join (
select ID, Data,
row_number() over (partition by ID order by Data) as rn
from #TestTable2
) t2 on t1.ID = t2.ID and t1.rn = t2.rn;
Note: Because of the LEFT JOIN, this does assume the amount of same ID's in table2 are equal or lower can those on table1. But you can change that to a FULL JOIN if that's not the case.
Returns :
ID Data Data2
1 r a
1 u q
2 t x
3 z c
To get the other result could have been achieved in different ways.
This is actually a more common situation.
Where one wants all from Table 1, but only get one value from Table 2 for each record of Table 1.
1) A top 1 with ties in combination with a order by rownumber()
select top 1 with ties
t1.ID, t1.Data,
t2.Data as Data2
from #TestTable1 t1
left join #TestTable2 t2 on t1.ID = t2.ID
order by row_number() over (partition by t1.ID, t1.Data order by t2.Data desc);
The top 1 with ties will only show those where the row_number() = 1
2) Using the row_number in a subquery:
select ID, Data, Data2
from (
select
t1.ID, t1.Data,
t2.Data as Data2,
row_number() over (partition by t1.ID, t1.Data order by t2.Data desc) as rn
from #TestTable1 t1
left join #TestTable2 t2 on t1.ID = t2.ID
) q
where rn = 1;
3) just a simple group by and a max :
select t1.ID, t1.Data, max(t2.Data) as Data2
from #TestTable1 t1
left join #TestTable2 t2 on t1.ID = t2.ID
group by t1.ID, t1.Data
order by 1,2;
All 3 give the same result:
ID Data Data2
1 r q
1 u q
2 t x
3 z c
Use ROW_NUMBER
DECLARE #Table1 TABLE (ID INT, DATA VARCHAR(10))
DECLARE #Table2 TABLE (ID INT, DATA VARCHAR(10))
INSERT INTO #Table1
VALUES
(1, 'r'),
(2, 't'),
(3, 'z'),
(1, 'u')
INSERT INTO #Table2
VALUES
(1, 'q'),
(1, 'a'),
(2, 'x'),
(3, 'c')
SELECT
A.*,
B.DATA
FROM
(SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY(SELECT NULL)) RowId FROM #Table1) A INNER JOIN
(SELECT *, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY(SELECT NULL)) RowId FROM #Table2) B ON A.ID = B.ID AND
A.RowId = B.RowId

Inner join then full join among the matched records

I have two tables:
declare #Table1 as table (id int, value CHAR(1))
declare #Table2 as table (id int, value CHAR(1))
INSERT #Table1
VALUES (1, 'A'),
(1, 'B'),
(3, 'A')
INSERT #Table2
VALUES(1, 'A'),
(1, 'C'),
(2, 'A')
I want to join these two tables so that at the end I should be able to produce this result:
id value id value
1 A 1 A
1 B NULL NULL
NULL NULL 1 C
I'm sorry for inadequate explanation (I mean no explanation at all). What I am trying to do here is (something like) to make inner join for the id columns (I mean take the records which are common on both sets over the "id" column) then look at the value columns and compare them inside the boundaries of this common set.
I hope I could describe what I was trying to do.
Hope this will work
SELECT distinct t1_id as id, t1_value as value , t2_id as id , t2_value as value
FROM (SELECT t1.id as t1_id, t1.value as t1_value from #Table1 t1 INNER JOIN #Table2 t2 on t1.id = t2.id) as A
FULL OUTER JOIN
(SELECT t2.id as t2_id, t2.value as t2_value from #Table1 t1 INNER JOIN #Table2 t2 on t1.id = t2.id) as B
on A.t1_value = B.t2_value
ORDER BY t1_id desc
Basically, What I am doing is outer joining the inner set (which is inner join on id column) on value column of inner set.
What do I win?
SELECT c.id,c.value,d.id,d.value
FROM
#Table1 c
full join
#Table2 d
on c.id = d.id and c.value = d.value
WHERE exists
(
SELECT a.id
FROM
#Table1 a
INNER JOIN
#Table2 b
ON a.id = b.id and a.id = c.id or d.id = a.id
)
You can do this with a full outer join and by pre-filtering the tables:
select a.id as a_id, a.value as a_value,
b.id as b_id, b.value as b_value
from (select *
from tablea a
where a.id = 1
) a full outer join
(select *
from tableb b
where b.id = 1
) b
on a.id = b.id and a.value = b.value;

SQL Server 2008 - Get Latest Record from Joined Table

I have a SQL Server 2008 database. This database has two tables called Customer and Order. These tables are defined as follows:
Customer
--------
ID,
First Name,
Last Name
Order
-----
ID,
CustomerID,
Date,
Description
I am trying to write a query that returns all of the customers in my database. If the user has placed at least one order, I want to return the information associated with the most recent order placed. Currently, I have the following:
SELECT
*
FROM
Customer c LEFT OUTER JOIN Order o ON c.[ID]=o.[CustomerID]
As you can imagine, this will return all of the orders associated with a customer. In reality though, I only want the most recent one. How do I do this in SQL?
Thank you!
Here's a method that doesn't assume that the order dates are unique:
SELECT
Customer.ID CustomerID,
Customer.FirstName,
Customer.LastName,
T1.ID OrderID,
T1.Date OrderDate,
T1.Description OrderDescription
FROM Customer
LEFT JOIN (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY CustomerId ORDER BY Date DESC) AS rn
FROM [Order]
) T1
ON Customer.ID = T1.CustomerID AND T1.rn = 1
Result:
CustomerID FirstName LastName OrderID OrderDate OrderDescription
1 FirstName1 LastName1 2 2010-05-02 Description2
2 FirstName2 LastName2 3 2010-05-03 Description3
3 FirstName3 LastName3 NULL NULL NULL
Test data:
CREATE TABLE Customer (ID INT NOT NULL, FirstName VARCHAR(100) NOT NULL, LastName VARCHAR(100) NOT NULL);
INSERT INTO Customer (ID, FirstName, LastName) VALUES
(1, 'FirstName1', 'LastName1'),
(2, 'FirstName2', 'LastName2'),
(3, 'FirstName3', 'LastName3');
CREATE TABLE [Order] (ID INT NOT NULL, CustomerID INT NOT NULL, Date DATE NOT NULL, Description NVARCHAR(100) NOT NULL);
INSERT INTO [Order] (ID, CustomerID, Date, Description) VALUES
(1, 1, '2010-05-01', 'Description1'),
(2, 1, '2010-05-02', 'Description2'),
(3, 2, '2010-05-03', 'Description3'),
(4, 2, '2010-05-03', 'Description4');
select c.ID, c.FirstName, c.LastName, o.ID as OrderID, o.Date, o.Description
from Customer c
left outer join (
select CustomerID, max(Date) as MaxDate
from Order
group by CustomerID
) om on c.ID = om.CustomerID
left outer join Order o on om.CustomerID = o.CustomerID and om.MaxDate = o.Date
I would use where clause with Max() function to guarantee the latest added record:
(you code...)
Where o.id = max(o.id)
Select * from
Customer C Left join
(
Select o.CustomerID, Description, Date from
Orders o inner join
(
Select CustomerID, Max(Date) as LastOrder
From Orders Group by CustomerID
) SubLatest on o.CustomerID = SubLatest.CustomerID
and o.Date = SubLatest.LastOrder
) SubDetails
on C.id = SubDetails.CustomerID
Not Homework I hope! :O)
Select Top 1 C.*
,O.*
From Customer C left outer join
Order O on O.CustomerId = C.Id
Order by O.[Date] Desc
Hope that helps

Resources