Query performances by evaluating Execution Plan (Join First Row) - sql-server

I have the following tables:
Customers
ID Name
============
1 John
2 Alice
3 Bob
Orders
ID CustomerID Status
==========================
1001 1 1
1002 2 1
1003 2 2
1004 3 2
I'd like to join tables showing one entry per customer only (the one with lowest Status) i.e.
ID Name OrderID
======================
1 John 1001
2 Alice 1002
3 Bob 1004
Thanks to the answer to this question, I chose 2 solutions which produce the same output:
Solution 1
SELECT c.id, c.name, o.id FROM customers AS c
INNER JOIN orders AS o ON
c.id = o.customerid
WHERE o.status = (SELECT MIN(status) FROM orders WHERE customerid = c.id)
Solution 2
SELECT c.id, c.name, o.id FROM customers as c
INNER JOIN orders AS o ON
o.id = (SELECT TOP 1 id FROM orders WHERE customerid = c.id ORDER BY status)
Trying to understand which one runs faster, I used SQL Fiddle View Execution Plan which gave the following:
Solution 1
Solution 2
How to interpret those diagrams and which one performs faster?
Using MS SQL Server 2016.

Here's my breakdown and the last one is my suggestion to you.
Query Cost 67%
SELECT c.id, c.name, o.id FROM #Customers AS c
INNER JOIN #Orders AS o ON
c.id = o.customerid
WHERE o.status = (SELECT MIN(status) FROM #Orders WHERE customerid = c.id)
Query Cost 66%
SELECT c.id, c.name, o.id FROM #Customers as c
INNER JOIN #Orders AS o ON
o.id = (SELECT TOP 1 id FROM #Orders WHERE customerid = c.id ORDER BY status)
Query Cost 47%
SELECT
x.CustID,
x.Name,
x.OrderID
FROM (SELECT
C.id CustID,
c.Name,
o.ID OrderID,
o.status,
ROW_NUMBER() OVER (PARTITION BY c.id ORDER BY o.status) rn
FROM #Customers c
INNER JOIN #Orders o
ON o.CustomerID = c.ID) x
WHERE x.rn = 1

Related

SQL Query to find deference between sum of two fields in different table

I have 3 tables as follows. I want to find balance
Table A
studentID Name
1 Bob
2 Sam
3 Sara
Table B
id studentID Credit
1 1 100
2 1 150
3 2 150
4 2 150
5 3 100
6 3 200
Table C
id studentID Amount Type
1 1 50 cash
2 1 120 card
3 2 100 cash
4 2 130 card
5 3 50 card
6 3 150 card
I want to get a result table as follows where Balance = Sum(credit) - sum(amount) where type = card.
result Table
studentID Name Credit Amount Balance
1 Bob 250 120 130
2 Sam 300 130 170
3 Sara 300 200 100
EDIT
According to his comment this is the query he has tried
select A.studentID,
A.Name,
ISNULL(SUM(B.Credit),0) as [Credit],
ISNULL(SUM(C.Amount),0) as [Amount],
ISNULL(SUM(B.Credit),0) - (select ISNULL(SUM(C.Amount),0) from C Group by C.studentID having C.Type='card' and C.studentID=A.studentID) as [balance]
from A
left outer join B on A.studentID = B.studentID
left outer join C on B.studentID = C.studentID
group by A.studentID
Handles students with no credit/amounts:
SELECT A.STUDENTID, A.[NAME]
, sum(B.CREDIT) AS [CREDIT]
, sum(C.AMOUNT) AS [AMOUNT]
, sum(B.CREDIT) - sum(C1.AMOUNT) AS [BALANCE]
FROM #TABLEA A
INNER JOIN (SELECT A.STUDENTID, isnull(SUM(B.CREDIT), 0) AS CREDIT FROM #TABLEA A LEFT JOIN #TABLEB B ON A.STUDENTID = B.STUDENTID GROUP BY A.STUDENTID) B ON A.STUDENTID = B.STUDENTID
INNER JOIN (SELECT A.STUDENTID, isnull(SUM(C.AMOUNT), 0) AS AMOUNT FROM #TABLEA A LEFT JOIN #TABLEC C ON A.STUDENTID = C.STUDENTID WHERE [TYPE] = 'CARD' GROUP BY A.STUDENTID) C ON B.STUDENTID = C.STUDENTID
INNER JOIN (SELECT A.STUDENTID, isnull(SUM(C.AMOUNT), 0) AS AMOUNT FROM #TABLEA A LEFT JOIN #TABLEC C ON A.STUDENTID = C.STUDENTID WHERE [TYPE] = 'CARD' GROUP BY A.STUDENTID) C1 ON B.STUDENTID = C1.STUDENTID
group by A.STUDENTID, A.[NAME]
This should work fine
SELECT
a.studentid,
a.Name,
sum(b.Credit) AS Credit,
ISNULL(c.Amount,0) AS Amount,
sum(b.Credit) - ISNULL(c.Amount,0) AS Balance
FROM a
INNER JOIN b ON a.studentid = b.StudentID
LEFT JOIN (select sum(amount) amount,studentid from c where type='card'
group by studentid) as c ON a.studentid = c.studentid
GROUP BY a.studentid, a.Name,c.Amount
SQL Fiddle Link
I haven't tried it. Please let me know.
SELECT A.studentID,A.Name,B.Credit,C.Amount,(Credit-Balance) as Amount from A, B, C where A.studentID=B.studentID=C.studentID AND B.id=C.id AND C.type="card";
This should work. It assumes all students in A are in B. The left join caters for the fact that not all may have a 'card' amount, and returns a 0 if they do not (otherwise a credit subtracted by a NULL will return NULL balance).
SELECT
a.StudentID,
a.Name,
sum(b.Credit) AS Credit,
ISNULL(sum(c.Amount),0) AS Amount,
sum(b.Credit) - ISNULL(sum(c.Amount),0) AS Balance
FROM tableA as a
INNER JOIN tableB as b ON a.StudentID = b.StudentID
LEFT JOIN tableC as c ON a.StudientID = c.StudentID AND c.[Type] = 'card'
GROUP BY a.StudentID, a.Name
This would be my start point. To be sure about the right calcs.
Then I'll start to refactor.
SELECT a.*,
(SELECT SUM(b.credit) FROM TableB b WHERE a.studentID = b.studentID) as Credit,
(SELECT SUM(c.amount) FROM TableC c WHERE a.studentID = c.studentID) as Amount,
((SELECT SUM(b.credit) FROM TableB b WHERE a.studentID = b.studentID)
-
(SELECT SUM(c.amount) FROM TableC c WHERE a.studentID = c.studentID)) as Balance
FROM TableA a;
I made a sqlFiddle: http://sqlfiddle.com/#!9/b86dc/7
You can try a query like below
See live demo
select A.*, credit, amount, balance= coalesce(credit,0) - coalesce(amount,0) from
[table A] A
left join
(
select
studentid,
credit= sum(credit)
from [table b] b
group by studentid
)B
on A.studentID= B.studentID
left join
(
select
studentid,
amount= sum(amount)
from [table C]
group by studentid
)C
on A.studentID=C.studentID

Error when using WHERE in Correlated Subquery

I have the following relationship: Bank -> Financing -> Contracts -> Supplier
I have to select all the Banks that is related to a Supplier, using the query below:
SELECT DISTINCT A.Name AS BankName, A2.Name as SupplierName
FROM Companies A
INNER JOIN Financing F ON F.IdFinancialCompany = A.Id
INNER JOIN Contracts C ON F.IdContract = C.Id
INNER JOIN Companies A2 ON C.IdSupplier = A2.Id
GROUP BY A.Name, A2.Name
So far, so good.
Now I have to list the number of contracts that are active and the number of total contracts, so I thought about including a subquery:
SELECT DISTINCT A.Name AS BankName, A2.Name as SupplierName,
(SELECT COUNT(C.Id)),
(SELECT COUNT(C.Id) WHERE C.ContractStatus = 1)
FROM Companies A
INNER JOIN Financing F ON F.IdFinancialCompany = A.Id
INNER JOIN Contracts C ON F.IdContract = C.Id
INNER JOIN Companies A2 ON C.IdSupplier = A2.Id
GROUP BY A.Name, A2.Name
Line 2 Works well, but I got na error in line 3:
Column 'Contracts.ContractStatus' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I expect the following result:
BANK NAME | SUPPLIER NAME | TOTAL CONTRACTS | ACTIVE CONTRACTS
Bank 1 | Supplier 1| 5 | 2
How can I achieve this? I'm using SQL Server 2014
Thanks in advance!
Remove the distinct since your group by and use CASE
SELECT A.Name AS BankName, A2.Name as SupplierName,
COUNT(C.Id),
SUM(CASE WHEN C.ContractStatus = 1 THEN 1 ELSE 0 END)
FROM Companies A
INNER JOIN Financing F ON F.IdFinancialCompany = A.Id
INNER JOIN Contracts C ON F.IdContract = C.Id
INNER JOIN Companies A2 ON C.IdSupplier = A2.Id
GROUP BY A.Name, A2.Name

one user many phones in a temp table

I need to show in a temporary table one user a many phone of that user, but I'm stuck
in the select, I need something like this:
user1 phone1 phone2 phone3 phone4 phone5
11816116-5 8555588 77877888 33254477 224474 45777885
this is the code that I'm trying:
select
phone As phonenum
Into #Tmp_phonenumber
From
clients_has_phones
where
user_number='11816116-5'
thanks in advance.
I can not think of a good way of doing the select statement other than by self joining on how ever many phone numbers your user may have.. With that being said you can try this for your select statement:
;With CTE_Main as (
Select
id
,Fono
,row_number()
Over(Partition by ID order by Fono) as RN
From sucursales
), CTE_Users as (
Select
id as id_num
from sucursales
group by id
)
Select
id_num
,a.Fono as Phone_1
,b.Fono as Phone_2
,c.Fono as Phone_3
,d.Fono as Phone_4
,e.Fono as Phone_5
From CTE_Users as realz
Left Join [CTE_Main] as a on a.id = realz.id_num and a.RN = 1
Left Join [CTE_Main] as b on b.id = realz.id_num and b.RN = 2
Left Join [CTE_Main] as c on c.id = realz.id_num and c.RN = 3
Left Join [CTE_Main] as d on d.id = realz.id_num and d.RN = 4
Left Join [CTE_Main] as e on e.id = realz.id_num and e.RN = 5
I know its kind of lengthy but it will display the results in the way that you want them.. My example only uses 5 rows but it should be pretty self explanatory.
Sql Fiddle: http://sqlfiddle.com/#!3/496f6/1

Group by According to three tables

I have three tables
tbl_worker
City WorkerName Job
10 Raymond Carpenter
10 Sara Programmer
11 John Painter
11 Alice Booker
tbl_workshop
City WorkshopName
10 Dell
11 Apple
10 Sony
Tbl_city
Id Name
10 Mexico
11 Sydney
Now I want such a result:
City WorkersQty WorkshopQry
Mexico 2 2
Sydney 2 1
The result returns the quantity of workers and workshops which are registered in different cities.
what query should I write?
Group the tbl_workshop and tbl_worker tables individually, then use a LEFT JOIN to get the results:
SELECT c.City,
k.Cnt AS WorkersQty,
w.Cnt AS WorkshopQry
FROM Tbl_city c
LEFT JOIN (
SELECT City, COUNT(*) Cnt
FROM tbl_worker
GROUP BY City
) k ON c.ID = k.City
LEFT JOIN (
SELECT City, COUNT(*) Cnt
FROM tbl_workshop
GROUP BY City
) w ON c.ID = w.City
If you want to only show the cities that have workshop's or workers, add:
WHERE k.Cnt > 0 OR w.Cnt > 0
Use Dense_rank to get the number of workshops or workers
SELECT city, MAX(WorkshopQty) AS WorkshopQty , MAX(WorkersQty) AS WorkersQty
FROM (
SELECT c.name AS City,
DENSE_RANK() OVER (PARTITION BY c.id ORDER BY WorkshopName) AS WorkshopQty,
DENSE_RANK() OVER (PARTITION BY c.id ORDER BY WorkerName) AS WorkersQty
FROM tbl_city c
INNER JOIN tbl_workshop s ON s.city = c.id
INNER JOIN tbl_worker w ON w.city = c.id
) s
GROUP BY City

Distinct records when joined with a table in SQL Server

How do I get top 1 record for each product from the query below?
SELECT DISTINCT o.product, o.orderID, od.qty, od.amount FROM
orders o, orderdetails od WHERE o.orderID=od.orderID
Product OrderID Qty Amount
Pen 11222 10 100.00
Pen 11223 5 50.00
Pen 11224 1 10.00
Book 22222 1 12.00
Book 2223 5 10.00
Scale 1111 2 9.00
Scale 2222 1 2.00
SELECT
o.Product, MAX(od.QTy)
FROM
orders o
INNER JOIN orderdetails od ON o.orderID=od.orderID
GROUP BY o.Product
or
SELECT
o.Product, MAX(od.Amount)
FROM
orders o
INNER JOIN orderdetails od ON o.orderID=od.orderID
GROUP BY o.Product
depending on what you consider as "top" product.
Top based on what? Most Quantity? if so try this
Select * From Orders O
Where OrderId =
(Select Max(orderId) From Orders
Where product = P.product
And Qty = (Select Max(Qty)
From orders
Where Product = O.Product))
Otherwise if Top means something else change last three lines to find Orderid for the record that satisfies whatever that definition of 'Top'
Give this a shot...
Select o.product, o.orderID, od.qty, od.amount
from orders o, orderdetails od
where o.orderID = od.orderID
and o.orderID in (Select Min(o.orderId)
From orders o
Group By o.product)

Resources