SQL Server fetch columns based on specific row numbers - sql

I have a requirement where I need to select last one year orders based on email (billing address email address). I need to show order number, shipping first name, shipping last name, billing address email id and item descriptions along with submitted date. Same address table use to store shipping (ship_to 1) and billing address (ship_to always 0). The email field in shipping address is optional and if both the addresses match only billing address is written to DB.
Address:
Order_num first_name last_name email ship_to_num
-----------------------------------------------------------
ord1 abc abc abc#c.com 0
ord1 cdf ccc XXc#m.com 1
I wrote a initial query like below:
select
addr.first_name, addr.last_name, addr.order_num, addr.Update_Date_Time,
ord.Order_Amt, item.Item_Desc
from
ADDR addr
inner join
ORDER ord on ord.Order_num = addr.Order_Num
inner join
ITEM item on ord.Order_num = item.Order_Num
where
addr.Order_Num in (select Order_num
from TOEADDR addr
where addr.ShipTo_Num = 0
and addr.EM_EMAIL_ADDR = 'avc#abc.com'
and addr.Update_Date_Time > DATEADD(year,-1, GETDATE()))
order by
addr.Update_Date_Time desc
Is there any way I can add this logic to above query:
(SELECT TOP 1 First_Name, LAST_NAME FROM ADDR ORDER BY ShipTo_Num ASC)
Thanks, with CTE it worked like a charm. Below is the final query:
;with cte_addr
AS
(SELECT First_Name,LAST_NAME, EM_EMAIL_ADDR, order_num,Update_Date_Time,ShipTo_Num
, ROW_NUMBER() OVER (PARTITION BY order_num ORDER BY ShipTo_Num desc) as ROWID
FROM ADDR
)
SELECT addr.first_name , addr.last_name, addr.order_num, addr.Update_Date_Time,
ord.Order_Amt, item.Item_Desc
FROM cte_addr addr
inner join ORDER ord on ord.Order_num = addr.Order_Num
inner join ITEM item on ord.Order_num = item.Order_Num
WHERE addr.ROWID =1 and
addr.Order_Num in(
select Order_num from ADDR billAddress where
billAddress.ShipTo_Num = 0 and billAddress.EM_EMAIL_ADDR = 'afv#abv.com'
and billAddress.Update_Date_Time > DATEADD(year,-1,GETDATE())
)
order by addr.Update_Date_Time desc

You can use CTE to achieve it. You can write query like below. This can be a sample query for the logic you are looking for
;with cte_addr
AS
(SELECT First_Name,LAST_NAME, email EM_EMAIL_ADDR, order_num,Update_Date_Time,ShipTo_Num
, ROW_NUMBER() OVER (PARTITION BY First_Name,LAST_NAME ORDER BY ShipTo_Num) ROWID
FROM ADDR
)
SELECT addr.first_name , addr.last_name, addr.order_num, addr.Update_Date_Time,
ord.Order_Amt, item.Item_Desc
FROM cte_addr addr
inner join ORDER ord on ord.Order_num = addr.Order_Num
inner join ITEM item on ord.Order_num = item.Order_Num
WHERE t.ROWID =1
and addr.Order_Num in(
select Order_num from TOEADDR addr where
addr.ShipTo_Num = 0 and addr.EM_EMAIL_ADDR = 'avc#avc.com'
and addr.Update_Date_Time > DATEADD(year,-1,GETDATE())
)
order by addr.Update_Date_Time desc

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

Row to column in SQL Server with dynamic number of rows

My source data looks something like this
ID Phone Type Phone Number
-------------------------------------
308820 P 8136542273
308820 F 8136541384
308820 P 8139108555
308820 P 8136842229
308820 F 8139108655
211111 P 6874598695
456788 F 5687659867
In the above data, Phone type = P is phone and F is fax.
I need to sort the data and then pick only one F type and one P type phone number and populate the data as below
ID Fax Number Phone Number
-------------------------------------
308820 8136541384 8136542273
211111 6874598695
456788 5687659867
Can someone help me out how to achieve this. There can be n number of Phone Numbers and Fax numbers against one ID but I need to sort it and pick the first.
You can use conditional aggregation:
select id, max(case when phone_type = 'F' then phone_number end) as fax,
max(case when phone_type = 'P' then phone_number end) as phone
from t
group by id;
Guys thanks for the response. The query which I created is below. The help I got above was used in my query below. However the problem is that in the Binary_CheckSum it randomly picks up a Phone number and as such again selects the same record.
What I am trying to find is that for a particular ID and AddressID combination has the iSActive flag or Phone Number or Fax number has changed so that I need to select that record and insert it in PrescriberLocation table.
select TESTID, ADDRESSID, max(PhoneNumber) as PhoneNumber,max(FaxNumber) as FaxNumber,'1' as isactive from (select distinct a.TESTID
, b.AddressId , CASE WHEN c.PhoneType = 'P'THEN C.PhoneNumber END PhoneNumber
, CASE WHEN c.PhoneType = 'F'THEN C.PhoneNumber END FaxNumber
,'1' as isactive from stg_Address a inner join stg_AddressPhone c on a.TESTID = c.TESTID and a.AddressID = c.AddressID INNER join pbmsys_new.dbo.sc_Address b on
upper(a.Address1) = upper(b.Address1) and upper(isnull(a.Address2,'')) = upper(b.Address2) join pbmsys_new.[dbo].[dr_PrescriberLocation] d
on a.TESTID = d.TESTID and b.AddressId = d.AddressId
where BINARY_CHECKSUM(1,c.PhoneNumber, FaxNumber) != BINARY_CHECKSUM(d.IsActive,d.PhoneNumber,d.FaxNumber) and d.PrescriberLocationId = SELECT max(Z.PrescriberLocationId) as PrescriberLocationId FROM pbmsys_new.dbo.dr_PrescriberLocation Z where d.TESTID = z.TESTID and d.AddressId =z.AddressId))f group by TESTID, AddressId
you can use aggregate function and sub-query
select id,max(fax_Number) as fax_Number, max(phone_Number) as phone_Number
from
(select id, case when phone_type = 'F' then phone_number end as fax_Number,
case when phone_type = 'P' then phone_number end as phone_Number
from yourtable
) as t group by id
If the requirement is to pick first value and not the max value then you can use it.
; with CTE1
As
(
select row_number() over (partition by id,phonetype order by ID) as Num,ID,
case when phonetype = 'F' then PhoneNumber end as FaxNumber,
case when phonetype = 'P' then PhoneNumber end as PhoneNumber
from ContactDetails
)
select ID,max(FaxNumber),max(PhoneNumber)
from (select ID,FaxNumber,PhoneNumber from CTE1 where num = 1)as T2
group by ID

Query in SQL Server 2014 for a report (I need the last ROW of a table)

I'm using SQL Server 2014 and I have a problem with a query.
I want to have in my report, ALL the items of the order with ID_Order = 9 that have been delivered. And for the items that have been delivered at two times (Item Code = Art3 for example), I just want to have the last row, that means the last delivery of this Item, with NO repetition.
I already tried these two queries without success:
Attempt #1: DISTINCT
SELECT DISTINCT
Order.ItemCode, Delivery. Qty, Delivery.ID_Delivery,
Order.ID_Order
FROM
Delivery
INNER JOIN
Order ON Order.ID_Order = Delivery.ID_Order
WHERE
Order.ID_Order = '9'
Attempt #2: subquery
SELECT *
FROM
(SELECT
Order.ItemCode, Delivery.Qty,
FROM
Delivery
INNER JOIN
Order ON Order.ID_Order = Delivery.ID_Order
WHERE
Order.ID_Order = '9')
GROUP BY
a.ItemCode, a.Qty
Try this query --
;WITH CTE
AS (
SELECT C.ID_Order
,D.ID_Delivery
,C.ItemCode
,C.Quantity
,ROW_NUMBER() OVER (
PARTITION BY C.ItemCode ORDER BY D.ID_Delivery DESC
) AS RowNum
FROM Customer_Order C
INNER JOIN Delivery D ON C.ID_Order = D.ID_Order
AND C.ItemCode = D.ItemCode
WHERE C.ID_Order = 9
)
SELECT ID_Order
,ID_Delivery
,ItemCode
,Quantity
FROM CTE
WHERE RowNum = 1
SELECT
Order.ItemCode, Delivery. Qty, Delivery.ID_Delivery,
Order.ID_Order
FROM
Delivery
INNER JOIN
Order ON Order.ID_Order = Delivery.ID_Order
WHERE
Order.ID_Order = '9'
AND Delivery.ID_Delivery IN
(
SELECT MAX(ID_Delivery) FROM Delivery D WHERE D.ID_Order = Delivery.ID_Order GROUP BY D.ID_Order
)
I hope it will work for you.

SQL query select record with Max

I have these records below :
CustomerID | Name | Store | Quantity
1 | Elie | HO | 16
1 | Elie | S1 | 4
I would like to filter customers by taking only their max quantity?
I tried it with Max, but the problem I cannot render all the fields with it. If I add main.store in the first line, the second row shows.
Is there any solution?
Select main.CUSTOMER_ID, main.Name
from
(
Select Name = cus.FIRST_NAME + ' ' + cus.LAST_NAME,
Store = cs.NAME
,Transaction_Number = count(ts.TRANSACTION_SUMMARY_ID)
,cus.CUSTOMER_ID
from TRANSACTION_SUMMARY ts
inner join dbo.CUSTOMER cus
on ts.CUSTOMER_ID = cus.CUSTOMER_ID
inner join dbo.CORPORATE_STORE cs
on ts.CORPORATE_STORE_ID = cs.CORPORATE_STORE_ID
Group by cus.CUSTOMER_ID
,cus.FIRST_NAME
,cus.LAST_NAME
,cs.Name
) as main
Group by CUSTOMER_ID
,main.Name
order by main.CUSTOMER_ID
This is a good use of window functions:
with t as (
Select Name = cus.FIRST_NAME + ' ' + cus.LAST_NAME,
Store = cs.NAME,
Transaction_Number = count(ts.TRANSACTION_SUMMARY_ID) , cus.CUSTOMER_ID
from TRANSACTION_SUMMARY ts
inner join dbo.CUSTOMER cus on ts.CUSTOMER_ID = cus.CUSTOMER_ID
inner join dbo.CORPORATE_STORE cs on ts.CORPORATE_STORE_ID = cs.CORPORATE_STORE_ID
Group by cus.CUSTOMER_ID, cus.FIRST_NAME, cus.LAST_NAME, cs.Name
)
select name, store, Transaction_Number, CUSTOMER_ID
from (select t.*,
row_number() over (partition by customer_id order by transaction_number desc) as seqnum
from t
) t
where seqnum = 1;
You can actually dispense with the subquery. However, using window functions with aggregations looks funny at first:
with t as (
Select Name = cus.FIRST_NAME + ' ' + cus.LAST_NAME,
Store = cs.NAME,
Transaction_Number = count(ts.TRANSACTION_SUMMARY_ID) , cus.CUSTOMER_ID,
row_number() over (partition by cus.CUSTOMER_ID
order by count(ts.TRANSACTION_SUMMARY_ID) desc
) as seqnum
from TRANSACTION_SUMMARY ts
inner join dbo.CUSTOMER cus on ts.CUSTOMER_ID = cus.CUSTOMER_ID
inner join dbo.CORPORATE_STORE cs on ts.CORPORATE_STORE_ID = cs.CORPORATE_STORE_ID
Group by cus.CUSTOMER_ID, cus.FIRST_NAME, cus.LAST_NAME, cs.Name
)
select name, store, Transaction_Number, CUSTOMER_ID
from t
where seqnum = 1;
Please try:
select * From tbl a
where a.Quantity=
(select MAX(b.Quantity) from tbl b where a.CustomerID=b.CustomerID)
what you want is
select customer_id, max( quantity )
from main
group by customer_id
then you can use this to join to itself if you want
select *
from main
, (
select customer_id, max( quantity ) qty
from main
group by customer_id
) m
where main.customer_id = m.customer_id
and main.quantity = m.qty
Obviously, name has no business being in this table, but you included it, so I did too...
SELECT x.*
FROM my_table x
JOIN
( SELECT customerid
, name
, MAX(quantity) max_quantity
FROM my_table
GROUP
BY customerid
, name
) y
ON y.customerid = x.customerid
AND y.name = x.name
AND y.max_quantity = x.quantity;

SQL Server 2005 - Row_Number()

I'm trying to understand the unusual behaviour seen when ordering results in a descending order using the row_number() function when using a DISITINCT on the outermost select in my query as below:
SELECT DISTINCT (ID), State_Id, Name_Of_Trip, Date_Of_Travel, Creation_Date, Locking_Id, Applicant_Name, Reference_Number, State_Name
FROM (
SELECT app.ID, app.State_Id, app.Name_Of_Trip, app.Date_Of_Travel, app.Creation_Date, app.Locking_Id, app.Applicant_Name, app.Reference_Number,
State.Name AS State_Name, ROW_NUMBER() OVER(ORDER BY Reference_Number DESC) as rowNum
FROM Application_Leg AS app
INNER JOIN State AS state
ON app.State_Id = state.ID
WHERE (app.State_Id = 5 OR app.State_Id = 6 OR app.State_Id = 8) AND app.Organisation_Id=12
AND Leg_Number IN
(SELECT DISTINCT Leg_Number
from Application_Leg as al
INNER JOIN
Organisation as org
ON al.Organisation_Id = org.ID
WHERE al.ID=app.ID AND org.Approval_Required=1 AND Mode_Of_Transport=1))
as pagedApplications
WHERE rowNum BETWEEN 0 AND (0 + 10)
When the outermost DISTINCT is taken out then the descending order is fine but when it is included the results are not shown in descending order.
ORDER BY in ROW_NUMBER clause does not guarantee the order of the resultset.
ROW_NUMBER usually uses sorting in the query plan which results in the fact that the values come out presorted.
This is a side effect and should not be relied upon.
DISTINCT uses Hash Match (Aggregate) which breaks sorting.
Add ORDER BY clause to the end of the query:
SELECT DISTINCT (ID), State_Id, Name_Of_Trip, Date_Of_Travel, Creation_Date, Locking_Id, Applicant_Name, Reference_Number, State_Name
FROM (
SELECT app.ID, app.State_Id, app.Name_Of_Trip, app.Date_Of_Travel,
app.Creation_Date, app.Locking_Id, app.Applicant_Name, app.Reference_Number,
State.Name AS State_Name, ROW_NUMBER() OVER(ORDER BY Reference_Number DESC) as rowNum
FROM Application_Leg AS app
INNER JOIN
State AS state
ON app.State_Id = state.ID
WHERE app.State_Id IN (5, 6, 8)
AND app.Organisation_Id = 12
AND Leg_Number IN
(
SELECT Leg_Number
FROM Application_Leg as al
INNER JOIN
Organisation as org
ON al.Organisation_Id = org.ID
WHERE al.ID = app.ID
AND org.Approval_Required = 1
AND Mode_Of_Transport = 1
)
) AS pagedApplications
WHERE rowNum BETWEEN 0 AND (0 + 10)
ORDER BY
ReferenceNumber DESC
Also note that it will not return 10 distinct results, it will return DISTINCT of the first 10 results.
If you want the former, use this:
SELECT DISTINCT TOP 10 ID, State_Id, Name_Of_Trip, Date_Of_Travel, Creation_Date, Locking_Id, Applicant_Name, Reference_Number, State_Name
FROM (
SELECT app.ID, app.State_Id, app.Name_Of_Trip, app.Date_Of_Travel,
app.Creation_Date, app.Locking_Id, app.Applicant_Name, app.Reference_Number,
State.Name AS State_Name
FROM Application_Leg AS app
INNER JOIN
State AS state
ON app.State_Id = state.ID
WHERE app.State_Id IN (5, 6, 8)
AND app.Organisation_Id = 12
AND EXISTS
(
SELECT Leg_Number
FROM Application_Leg AS al
INNER JOIN
Organisation as org
ON al.Organisation_Id = org.ID
WHERE al.ID = app.ID
AND al.LegNumber = app.LegNumber
AND org.Approval_Required = 1
AND Mode_Of_Transport = 1
)
) AS pagedApplications
ORDER BY
ReferenceNumber DESC
Have you tried adding an order by to your outer select?