Struggling with left join - sql

I'm struggling with left joining the earliest row in this left join.
The results are showing a 2011 date, but i know for a fact this particular row should be returning 2008.
SELECT TOP 1000
f.name as [Franchisee]
,p.paid_date as paid_date
FROM franchisees_franchisee f
OUTER APPLY (SELECT TOP 1 *
FROM era_project_invoice_payment p
WHERE f.franchiseeid = p.franchiseeid
and p.deleted = 0 and p.payment_confirmed = 1
ORDER BY p.eraprojectinvoicepaymentid ASC) p
where
f.deleted = 0
and f.name LIKE '%VKlinkosch%'
Below returns the correct, 2008 date.
SELECT TOP 1000
f.name as [Franchisee]
,min(p.paid_date) as paid_date
from [era_uat_shared].[dbo].[franchisees_franchisee] f
left join era_project_invoice_payment p
on f.franchiseeid = p.franchiseeid
where f.deleted = 0
and f.name LIKE '%VKlinkosch%'
GROUP BY f.name
Problem is, I need more than just the Paid Date from the payments table! :(

SELECT
f.name as [Franchisee]
, p.*
FROM franchisees_franchisee f
INNER JOIN
(
SELECT
ROW_NUMBER() OVER (PARTITION BY franchiseeid ORDER BY paid_date ASC) rn
, p.*
FROM
era_project_invoice_payment p
WHERE
deleted = 0
AND payment_confirmed = 1
) p
ON
f.franchiseeid = p.franchiseeid
AND f.deleted = 0
AND f.name LIKE '%VKlinkosch%'
AND p.rn = 1

Related

How I can select highest review from a user?

I need to select reviews for product, but unique by user (i.e. one review from user).
With my code, I select all reviews, and I can see few reviews left by one user.
SELECT
tr.reviewText, tr.reviewDate, tr.reviewRating,
u.userName AS userName,
u.userFirstName AS userFirstName, u.userSurname AS userSurname,
u.countryId AS countryId
FROM
tblReviews tr
INNER JOIN
tblOrderProduct op ON op.orderProductId = tr.orderProductId
AND op.productOptionId IN (SELECT productOptionId
FROM tblProductOption
WHERE productSubCuId = 111
AND productOptionActive = 1)
LEFT JOIN
tblOrder o ON o.orderId = op.orderId
LEFT JOIN
tblUser u ON u.userRandomId = o.userRandomId
WHERE
tr.reviewsStatusId = 2
ORDER BY
tr.reviewRating DESC, tr.reviewDate DESC
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
Can I get just one review from each user?
Maybe I need select userId -> group results by userId and select one per group? [I tried to do so, but I didn't succeed :( ]
You can use row_number to number the reviews and select any one like below:
;with per_user_one_review
as
(SELECT tr.reviewText, tr.reviewDate, tr.reviewRating,
u.userName as userName,
u.userFirstName as userFirstName, u.userSurname as userSurname,
u.countryId as countryId, row_number() over (partition by u.userRandomId order by tr.reviewDate desc) rn
FROM tblReviews tr
INNER JOIN tblOrderProduct op
ON op.orderProductId = tr.orderProductId
AND op.productOptionId IN (
SELECT productOptionId FROM tblProductOption
WHERE productSubCuId = 111 AND productOptionActive = 1
)
LEFT JOIN tblOrder o ON o.orderId = op.orderId
LEFT JOIN tblUser u ON u.userRandomId = o.userRandomId
WHERE tr.reviewsStatusId = 2
ORDER BY tr.reviewRating DESC, tr.reviewDate DESC
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
)
select * from per_user_one_review where rn = 1
It will pick the latest review (reviewDate desc) from the user.
If you need the last review you could use a join with the suquery for max review date grouped by orderProductId
(and as a suggestion you could use a inner join instead of a IN clasue based on a subquery)
select tr.reviewText
, tr.reviewDate
, tr.reviewRating
, u.userName
, u.userFirstName
, u.userSurname
, u.countryId
from tblReviews tr
INNER JOIN (
select max(reviewDate) max_date, orderProductId
from tblReviews
group by orderProductId
) t1 on t1.orderProductId = tr.orderProductId and t1.max_date = tr.reviewDate
INNER JOIN tblOrderProduct op ON op.orderProductId = tr.orderProductId
INNER JOIN (
SELECT productOptionId
FROM tblProductOption
WHERE productSubCuId = 111 AND productOptionActive = 1
) t2 ON op.productOptionId = t2.productOptionId
LEFT JOIN tblOrder o ON o.orderId = op.orderId
LEFT JOIN tblUser u ON u.userRandomId = o.userRandomId
WHERE tr.reviewsStatusId = 2
ORDER BY tr.reviewRating DESC, tr.reviewDate DESC
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY

How to properly join TOP 1 fields to SQL query

How can I properly join the fields I have in the comment block to the SQL query? I have a billing phone number on the order header, but on the order lines, there is a shipping phone number for each line. The billing and shipping can be different.
On every order line of one order, it's 99% the same shipping number, but I want to do Top 1 and not group by just incase some data gets messed up.
I think UNION might get me what I want, but it seems like there is a better way to get everything in one query and not copying & pasting the same "where" clauses.
SELECT a.Order_no
,a.Customer_no
,a.BILL_LAST_NAME
,a.BILL_FIRST_NAME
,b.email
,a.BILL_ADDRESS1
,a.BILL_ADDRESS2
,a.BILL_CITY
,a.BILL_STATE
,a.BILL_POSTAL_CODE
,a.BILL_COUNTRY
,b.Address_Type
,a.BILL_PHONE
,a.BILL_PHONE_EXT
,a.Order_Date
,a.billing_status
,a.PO_Number
,a.Customer_comments
,a.ShipMethodShipperDesc
,a.ShipRate
,a.CouponDiscountCode
,a.CouponDiscount
,a.CustomerDiscount
,a.CustomerDiscountPercent
,a.SalesTaxTotal
,a.Payment_Method
,a.Credit_Card_Type
,a.Credit_Card_Number
,a.Order_Date
,a.BILL_TYPE
,a.Order_Net
/* I added these lines but would like them joined properly */
/*-------->*/
, (select top 1 SHIP_ADDRESS1 from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_ADDRESS2 from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_CITY from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_STATE from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_POSTAL_CODE from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_COUNTRY from LineItems C where c.ORDER_NO = a.ORDER_NO)
/*<-----------*/
FROM Orders AS a
,Customers AS b
WHERE a.customer_no = b.customer_no
AND a.AccountName = 'mywebaccount'
AND a.billing_status <> 'Canceled'
AND a.transferred = 0
AND a.order_status <> 'Canceled'
AND EXISTS (
SELECT *
FROM LineItems c
WHERE c.order_no = a.order_no
)
ORDER BY a.order_date
,a.order_no
Firstly, don't do FROM sometable1 AS t1, sometable2 AS t2. Always join explicitly. Secondly, from the way your case looks, some variant of APPLY would suit nicely.
Here's my version:
SELECT a.Order_no
,a.Customer_no
,a.BILL_LAST_NAME
,a.BILL_FIRST_NAME
,b.email
,a.BILL_ADDRESS1
,a.BILL_ADDRESS2
,a.BILL_CITY
,a.BILL_STATE
,a.BILL_POSTAL_CODE
,a.BILL_COUNTRY
,b.Address_Type
,a.BILL_PHONE
,a.BILL_PHONE_EXT
,a.Order_Date
,a.billing_status
,a.PO_Number
,a.Customer_comments
,a.ShipMethodShipperDesc
,a.ShipRate
,a.CouponDiscountCode
,a.CouponDiscount
,a.CustomerDiscount
,a.CustomerDiscountPercent
,a.SalesTaxTotal
,a.Payment_Method
,a.Credit_Card_Type
,a.Credit_Card_Number
,a.Order_Date
,a.BILL_TYPE
,a.Order_Net
,li.SHIP_ADDRESS1
,li.SHIP_ADDRESS2
,li.SHIP_CITY
,li.SHIP_STATE
,li.SHIP_POSTAL_CODE
,li.SHIP_COUNTRY
FROM Orders AS a
INNER JOIN Customers AS b
ON a.customer_no = b.customer_no
CROSS APPLY
(
SELECT TOP 1 c.SHIP_ADDRESS1
,c.SHIP_ADDRESS2
,c.SHIP_CITY
,c.SHIP_STATE
,c.SHIP_POSTAL_CODE
,c.SHIP_COUNTRY
FROM LineItems c
WHERE c.ORDER_NO = a.ORDER_NO
ORDER BY c.Id -- or whatever
) AS li
WHERE a.AccountName = 'mywebaccount'
AND a.billing_status <> 'Canceled'
AND a.transferred = 0
AND a.order_status <> 'Canceled'
-- no need for that exists since CROSS APPLY works like INNER JOIN
ORDER BY a.order_date,a.order_no
I suggest something like:
SELECT Order_no
,Customer_no
,BILL_LAST_NAME
,BILL_FIRST_NAME
...
,SHIP_ADDRESS1
,SHIP_ADDRESS2
,SHIP_CITY
,SHIP_STATE
,SHIP_POSTAL_CODE
,SHIP_COUNTRY
FROM (
SELECT a.Order_no
,a.Customer_no
,a.BILL_LAST_NAME
,a.BILL_FIRST_NAME
,b.email
,a.BILL_ADDRESS1
,a.BILL_ADDRESS2
,a.BILL_CITY
,a.BILL_STATE
,a.BILL_POSTAL_CODE
,a.BILL_COUNTRY
,b.Address_Type
,a.BILL_PHONE
,a.BILL_PHONE_EXT
,a.Order_Date
,a.billing_status
,a.PO_Number
,a.Customer_comments
,a.ShipMethodShipperDesc
,a.ShipRate
,a.CouponDiscountCode
,a.CouponDiscount
,a.CustomerDiscount
,a.CustomerDiscountPercent
,a.SalesTaxTotal
,a.Payment_Method
,a.Credit_Card_Type
,a.Credit_Card_Number
,a.Order_Date
,a.BILL_TYPE
,a.Order_Net
,c.SHIP_ADDRESS1
,c.SHIP_ADDRESS2
,c.SHIP_CITY
,c.SHIP_STATE
,c.SHIP_POSTAL_CODE
,c.SHIP_COUNTRY
, row_number() over (partition by a.order_no
order by SHIP_ADDRESS1, SHIP_ADDRESS2, SHIP_CITY, SHIP_STATE
, SHIP_POSTAL_CODE, SHIP_COUNTRY) as rn
FROM Orders AS a
JOIN Customers AS b
ON a.customer_no = b.customer_no
JOIN numberLineItems c
ON c.order_no = a.order_no
WHERE a.AccountName = 'mywebaccount'
AND a.billing_status <> 'Canceled'
AND a.transferred = 0
AND a.order_status <> 'Canceled'
) as x
WHERE rn = 1
ORDER BY order_date
, order_no;
I created a simple example and posted
HERE on SQL Fiddle
The simplest way is just to join with the top 1 from a subquery
select cus.*, ord.*, sub.*
from Customer cus
join [Order] ord on ord.CustomerId = cus.Id
join (
select it.OrderId, it.ItemName, it.ItemAddress
from Items it
where it.Id in (select MAX(id) from Items group by OrderId)
) as sub on sub.OrderId = ord.Id
If you want to see the first item instead the last you can replace MAX(id) with MIN(id)
Note the only "problem" is if there are no items for the order it ill not show.
But if you got orders without a item and still want to show it you can use a left join
Se the fiddle for a running example.
I hope you don't mind I used explicit joins =)

SQL join two simple query with count and groupby

hello i have 2 queries and i wanna join together but i don't know how...
SELECT *, count(*) as invii
FROM professionisti JOIN preventivi_invii ON
professionisti.email=preventivi_invii.email
GROUP BY professionisti.email
HAVING invii> 300
SELECT *, count(*) as acquisti
FROM professionisti JOIN contatti_acquistati ON
professionisti.email=contatti_acquistati.email
GROUP BY professionisti.email
HAVING acquisti> 5
the problem for me is multiple count and the group by with same column.
thank u
How about the below query. You would just change the WHERE clause to meet your needs.
SQL Fiddle Example:
SELECT * FROM
(
SELECT p.email,
CASE WHEN ISNULL(m1.invii) THEN 0 ELSE m1.invii END AS invii,
CASE WHEN ISNULL(m2.acquisti) THEN 0 ELSE m2.acquisti END AS acquisti
FROM professionisti p
LEFT JOIN
(
SELECT pp.email, COUNT(*) AS invii
FROM preventivi_invii pp
GROUP BY pp.email
) AS m1 ON p.email = m1.email
LEFT JOIN
(
SELECT c.email, COUNT(*) AS acquisti
FROM contatti_acquistati c
GROUP BY c.email
) AS m2 ON p.email = m2.email
) AS mm
WHERE mm.invii = 0
OR mm.acquisti = 0;
Or you could use:
SELECT * FROM
(
SELECT p.email,
(
SELECT
CASE WHEN ISNULL(COUNT(*)) THEN 0 ELSE COUNT(*) END
FROM preventivi_invii pp
WHERE pp.email = p.email
) AS invii,
(
SELECT
CASE WHEN ISNULL(COUNT(*)) THEN 0 ELSE COUNT(*) END
FROM contatti_acquistati c
WHERE c.email = p.email
) AS acquisti
FROM professionisti p
) AS mm
WHERE mm.invii = 0
OR mm.acquisti = 0

(ORDER BY CASE WHEN) ordering by subquery

I need to order my results by int column ascending, but I want to get only rows with numbers (0...10000) but default ordering gives me rows with null values for this column before numbers. I googled solution which set rows with null into the end of ordering (after all numbers) it looks like
SELECT ProductName
FROM Products
ORDER BY
CASE WHEN Position is null THEN 1 ELSE 0 END,
Position
So I my query looks like:
SELECT c.CompanyId, c.CompanyName, c.CompanyCategoryId, cc.CompanyCategoryName, c.HQCountryISO, c.CrunchBaseUrl,c.AngelListUrl,
(SELECT MAX(mf.NumLikes) FROM MeasurementFacebook mf
JOIN FacebookAccount f ON f.CompanyId = c.CompanyId
WHERE f.FacebookAccountId in (mf.FacebookAccountId)) as Likes,
(SELECT MAX(mt.NumFollowers) FROM MeasurementTwitter mt
JOIN TwitterAccount t ON t.CompanyId = c.CompanyId
WHERE t.TwitterAccountId in (mt.TwitterAccountId)) as Followers,
(SELECT MAX(ma.AlexaRanking) FROM MeasurementAlexa ma
JOIN Website w ON w.CompanyId = c.CompanyId
WHERE w.WebsiteId in (ma.WebsiteId)) as AlexaRank
FROM Company c
JOIN CompanyCategory cc ON c.CompanyCategoryId = cc.CompanyCategoryId
WHERE c.HQCountryISO = 'FRA'
ORDER BY CASE WHEN AlexaRank IS NULL THEN 1 ELSE 0 END, AlexaRank
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
As you can see, AlexaRank is the result of third subquery, and I want to order result by this column. But I have an error which says:
Msg 207, Level 16, State 1, Line 14
Invalid column name 'AlexaRank'.
What I'm doing wrong? Thanks
While you can use an alias in the ORDER BY clause, you can't use an alias in an expression, easiest solution is to plop it in a cte/subquery:
;WITH cte AS (SELECT c.CompanyId
, c.CompanyName
, c.CompanyCategoryId
, cc.CompanyCategoryName
, c.HQCountryISO
, c.CrunchBaseUrl
,c.AngelListUrl
,(SELECT MAX(mf.NumLikes)
FROM MeasurementFacebook mf
JOIN FacebookAccount f ON f.CompanyId = c.CompanyId
WHERE f.FacebookAccountId in (mf.FacebookAccountId)) as Likes
,(SELECT MAX(mt.NumFollowers)
FROM MeasurementTwitter mt
JOIN TwitterAccount t ON t.CompanyId = c.CompanyId
WHERE t.TwitterAccountId in (mt.TwitterAccountId)) as Followers
,(SELECT MAX(ma.AlexaRanking)
FROM MeasurementAlexa ma
JOIN Website w ON w.CompanyId = c.CompanyId
WHERE w.WebsiteId in (ma.WebsiteId)) as AlexaRank
FROM Company c
JOIN CompanyCategory cc ON c.CompanyCategoryId = cc.CompanyCategoryId
WHERE c.HQCountryISO = 'FRA')
SELECT *
FROM cte
ORDER BY CASE WHEN AlexaRank IS NULL THEN 1 ELSE 0 END, AlexaRank
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
Very inefficient code but you could do something like the following. Basically wrap your initial query in a common table expression so you don't need to rewrite your 3rd sub-select in your order by.
SELECT * FROM (
SELECT c.companyid,
c.companyname,
c.companycategoryid,
cc.companycategoryname,
c.hqcountryiso,
c.crunchbaseurl,
c.angellisturl,
(SELECT Max(mf.numlikes)
FROM measurementfacebook mf
JOIN facebookaccount f
ON f.companyid = c.companyid
WHERE f.facebookaccountid IN ( mf.facebookaccountid )) AS Likes,
(SELECT Max(mt.numfollowers)
FROM measurementtwitter mt
JOIN twitteraccount t
ON t.companyid = c.companyid
WHERE t.twitteraccountid IN ( mt.twitteraccountid )) AS Followers,
(SELECT Max(ma.alexaranking)
FROM measurementalexa ma
JOIN website w
ON w.companyid = c.companyid
WHERE w.websiteid IN ( ma.websiteid )) AS AlexaRank
FROM company c
JOIN companycategory cc
ON c.companycategoryid = cc.companycategoryid
WHERE c.hqcountryiso = 'FRA' ) Q
ORDER BY CASE
WHEN Q.AlexaRank IS NULL THEN 1
ELSE 0
END,
Q.AlexaRank

How to use a column from joined table in a join with a subquery

What I'm trying to do is this:
SELECT *
FROM MainTable m
INNER JOIN JoinedTable j on j.ForeignID = m.ID
INNER JOIN (SELECT TOP 1 *
FROM SubQueryTable sq
WHERE sq.ForeignID = j.ID
ORDER BY VersionColumn DESC)
So basically, from SubQueryTable, I only want to retrieve a single row which has the maximum value for VersionColumn for all rows with a certain ID that I can get from JoinedTable.
T-SQL doesn't let me do this, what's a good way to solve this problem?
What I'm trying to prevent is loading the entire SubQueryTable and doing the filtering when it's too late as in....
SELECT *
FROM MainTable m
INNER JOIN JoinedTable j on j.ForeignID = m.ID
INNER JOIN (SELECT TOP 1 *
FROM SubQueryTable sq
ORDER BY VersionColumn DESC) sj ON sj.ForeignID = j.ID
I fear this second version performs the very slow subquery first and only filters it when it has loaded all the rows, but I want to filter sooner.
Any thoughts?
This will perform well if you have index on VersionColumn
SELECT *
FROM MainTable m
INNER JOIN JoinedTable j on j.ForeignID = m.ID
CROSS APPLY (SELECT TOP 1 *
FROM SubQueryTable sq
WHERE sq.ForeignID = j.ID
ORDER BY VersionColumn DESC) sj
Answer :
Hi,
Below query I have created as per your requirement using Country, State and City tables.
SELECT * FROM (
SELECT m.countryName, j.StateName,c.CityName , ROW_NUMBER() OVER(PARTITION BY c.stateid ORDER BY c.cityid desc) AS 'x'
FROM CountryMaster m
INNER JOIN StateMaster j on j.CountryID = m.CountryID
INNER JOIN dbo.CityMaster c ON j.StateID = c.StateID
) AS numbered WHERE x = 1
Below is your solution and above is only for your reference.
SELECT * FROM (
SELECT m.MainTablecolumnNm, j.JoinedTablecolumnNm,c.SubQueryTableColumnName , ROW_NUMBER()
OVER(PARTITION BY sj.ForeignID ORDER BY c.sjID desc) AS 'abc'
FROM MainTable m
INNER JOIN JoinedTable j on j.ForeignID = m.ID
INNER JOIN SubQueryTable sj ON sj.ForeignID = j.ID
) AS numbered WHERE abc = 1
Thank you,
Vishal Patel