SQL retrieve rows based on column condition - sql

Say I have below tables
Order Table
OrderNo CategoryID CountryID ServiceTypeID
100 1 3 1
200 2 5 2
300 3 4 4
400 1 2 9
1 service Type might belong to many category type
Category Table
ID Name ServiceTypeID
1 x 1
2 x 2
3 x 1
ServiceType table
ID Name
1 xx
2 xx
3 xx
Tracking Table
OrderNo CountryID TrackingTypeID
100 2 3
200 1 4
100 3 2
400 5 1
200 2 6
Reviewd Table
OrderNo
300
100
200
I want to write a query with below requirements
Order must belong to serviceTypeID = 1 or 2
And If the orderNo has a categoryID = 1
I want that record to be retrieved only if
there's a record in tracking table for that
orderNo with same countryID as in Order table
and if that orderNo doesn't have tracking type of id (0,7,1) in tracking table
Else for all other orders with any other category excluding orders which are not belong
to serviceTypeID = (1,2)
I want that record to be retrived only if
there's an existing record for that orderNo in
Reviewed table
and if that orderNo doesn't have tracking type of id (0,7,1) in tracking table
So basically based on above requirements the result should look like
OrderNo CategoryID StationID
100 1 3
200 2 5
select DISTINCT top 10000 o.orderNo , o.categoryID , o.serviceTypeid
,o.countrtId , Tracking.countryId
from Order o
join Tracking on o.orderNo = Tracking.orderNo
where
(o.CategoryID in (1 ) and o.countryId = Tracking.countryId
and
exists (select 1
from tracking t
where t.orderNo = o.orderNo and t.countryId =
o.countryId
)
)
OR
(o.categoryID in (select id from Category where ServiceTypeid in (7,8) and
ID not in (56 , 65)
) and
exists (select 1
from Reviewed r
where r.orderNo = o.orderNo
)
)
AND not exists
( select 1
from tracking t
WHERE T.orderNo = o.orderNo
and t.TrackingTypeID in (0 , 7 ,25))
That query seems to return only orders with ID 1 and even if it's have a trackingTypeID = 0,7,25

You could use exists and boolean logic:
select o.*
from orders o
where
(
category_id = 1
and exists (select 1 from tracking t where t.order_no = o.order_no and t.country_id = o.country_id)
)
or (
category_id = 2
and exists (select 1 from reviewed r where r.order_no = o.order_no)
)

Based on your conditions, you can use boolean logic with exists:
select o.*
from orders o
where (o.categoryid = 1 and
exists (select 1
from tracking t
where t.orderno = o.orderno and t.CountryID = o.CountryID
)
) or
(o.categoryid = 2 and
exists (select 1
from reviewed r
where r.orderno = o.orderno
)
) ;

According to your conditions this query will give results accordingly:
Select o.OrderNo,o.CategoryID,o.CountryID as StationID
from Order o
inner join Tracking t on t.OrderNo = o.OrderNo and t.CountryID = o.CountryID
where o.CategoryID = 1 or (o.CategoryID = 2
and exists (Select * from Reviewed where OrderNo = o.OrderNo))

Related

SQL Select Distinct Records From Two Tables

I am trying to write a SQL statement that will return a set of Distinct set of CompanyNames from a table based on the most recent SaleDate withing a specified date range from another table.
T01 = Account
T02 = TransHeader
The fields of importance are:
T01.ID, T01.CompanyName
T02.AccountID, T02.SaleDate
T01.ID = T02.AccountID
What I want to return is the Max SaleDate for each CompanyName without any duplicate CompanyNames and only the Max(SaleDate) as LastSale. I will be using a Where Clause to limit the SaleDate range.
I tried the following but it returns all the records for all SalesDates in the range. This results in the same company being listed multiple times.
Current MS-SQL Query
SELECT T01.CompanyName, T02.LastSale
FROM
(SELECT DISTINCT ID, IsActive, ClassTypeID, CompanyName FROM Account) T01
FULL OUTER JOIN
(SELECT DISTINCT AccountID, TransactionType, MAX(SaleDate) LastSale FROM TransHeader group by AccountID, TransactionType, SaleDate) T02
ON T01.ID = T02.AccountID
WHERE ( ( T01.IsActive = 1 )AND
( (Select Max(SaleDate)From TransHeader Where AccountID = T01.ID AND TransactionType in (1,6) AND SaleDate is NOT NULL)
BETWEEN '01/01/2016' AND '12/31/2018 23:59:00' AND (Select Max(SaleDate)From TransHeader Where AccountID = T01.ID AND TransactionType in (1,6) AND SaleDate is NOT NULL) IS NOT NULL
)
)
ORDER BY T01.CompanyName
I thought the FULL OUTER JOIN was the ticket but it did not work and I am stuck.
Sample data Account Table (T01)
ID CompanyName IsActive ClassTypeID
1 ABC123 1 1
2 CDE456 1 1
3 EFG789 1 1
4 Test123 0 1
5 Test456 1 1
6 Test789 0 1
Sample data Transheader table (T02)
AccountID TransactionType SaleDate
1 1 02/03/2012
2 1 03/04/2013
3 1 04/05/2014
4 1 05/06/2014
5 1 06/07/2014
6 1 07/08/2015
1 1 08/09/2016
1 1 01/15/2016
2 1 03/20/2017
2 1 03/21/2017
3 1 03/04/2017
3 1 04/05/2018
3 1 05/27/2018
4 1 06/01/2018
5 1 07/08/2018
5 1 08/01/2018
5 1 10/11/2018
6 1 11/30/2018
Desired Results
CompanyName LastSale (Notes note returned in the result)
ABC123 01/15/2016 (Max(SaleDate) LastSale for ID=1)
CDE456 03/21/2017 (Max(SaleDate) LastSale for ID=2)
EFG789 05/27/2018 (Max(SaleDate) LastSale for ID=3)
Testing456 10/11/2018 (Max(SaleDate) LastSale for ID=5)
ID=4 & ID=6 are note returned because IsActive = 0 for these records.
One option is to select the maximum date in the select clause.
select
a.*,
(
select max(th.saledate)
from transheader th
where th.accountid = a.id
and th.saledate >= '2016-01-01'
and th.saledate < '2019-01-01'
) as max_date
from account a
where a.isactive = 1
order by a.id;
If you only want to show transaction headers with sales dates in the given date range, then you can just inner join the maximum dates with the accounts. In order to do so, you must group your date aggregation per account:
select a.*, th.max_date
from account a
join
(
select accountid, max(saledate) as max_date
from transheader
and saledate >= '2016-01-01'
and saledate < '2019-01-01'
group by accountid
) th on th.accountid = a.id
where a.isactive = 1
order by a.id;
select CompanyName,MAX(SaleDate) SaleDate from Account a
inner join Transheader b on a.id = b.accountid
group by CompanyName
order by 1

Filter out entire group based on item ranking in SQL

I have a table as shown below:
group item rank
1 A 1
1 B 2
1 C 3
2 A 2
2 B 1
3 A 1
3 C 2
I want those groups data only, where item A has rank 1 as shown below:
group item rank
1 A 1
1 B 2
1 C 3
3 A 1
3 C 2
In group 2, A has rank 2, therefore not a part of output.
One way is using an IN clause
select *
from yourTable
where id in (select id from yourtable where item='A' and rank = 1)
you could use a subquery for get the involved id and the join
select * from my_table m
inner join (
select distinct id
from my_table
where item = 'A'
and rank = 1
) t on t.id = m.id

SQL update all records except the last one with a value

I need to make a query where only the last line of each user that has a car gets a license plate number.
ID UserId LicensePlate HasCar
1 1 ABC123 1
2 1 ABC123 1
3 2 NULL 0
4 3 UVW789 1
5 3 UVW789 1
Should become:
ID UserId LicensePlate HasCar
1 1 NULL 1
2 1 ABC123 1
3 2 NULL 0
4 3 NULL 1
5 3 UVW789 1
So I basically need to find all users with a licenseplate and change all but the last one and make the LicensePlate NULL
Assuming the ID column is an identity column so it can provide the ordering, something like this should do the trick:
;WITH CTE AS
(
SELECT Id,
UserId,
LicensePlate,
ROW_NUMBER() OVER(PARTITION BY UserId ORDER BY Id DESC) rn
FROM Table
WHERE HasCar = 1
)
UPDATE CTE
SET LicensePlate = NULL
WHERE rn > 1
You can try this
UPDATE l
SET l.LicensePlate = null
FROM Car l
INNER JOIN (SELECT UserId, Max(Id) AS max_id
FROM Car
GROUP BY UserId) m ON m.UserId = l.UserId
AND m.max_id <> l.id
You can do it with a join on the table itself like that :
UPDATE car c
INNER JOIN car c2 ON c.userId = c2.userId AND c.id < c2.id AND c.HasCar = 1 AND c2.HasCar = 1
SET c.LicensePlate = NULL
The condition c.id < c2.id will avoid to select the last line
By using LAG Function also you can achieve it.
;WITH License(ID,UserId,LicensePlate,HasCar)
as
(
SELECT 1,1,'ABC123',1 UNION ALL
SELECT 2,1,'ABC123',1 UNION ALL
SELECT 3,2,NULL ,0 UNION ALL
SELECT 4,3,'UVW789',1 UNION ALL
SELECT 5,3,'UVW789',1
)
SELECT ID,UserId,LAG(LicensePlate,1,NULL) OVER(PARTITION BY UserId ORDER BY LicensePlate),HasCar FROM License

T-SQL group by 2 tables

My tables looks like this:
For each TimeOfDay I would like to get the most frequent Category. For example if there are 3 auctions with unique ClosedTime but each of this Time has TimeOfDay=1 and 2 of these auctions have CategoryId=1 and one auction CategoryId=2 I would like to get:
TimeOfDay | CategoryId
1 | 1
I have tried group by TimeOfDay and CategoryId but still I don't know how to get top category for each TimeOfDay group. I have this:
select t.TimeOfDay, a.CategoryId, count(a.CategoryId)
numberOfSalesInCategory
from Auction a
join Time t on t.Id = a.ClosedTime
where IsSuccess = 1
group by t.TimeOfDay, a.CategoryId
and result for some sample data:
TimeOfDay | CategoryId | numberOfSalesInCategory
0 1 1
1 1 1
1 2 3
2 2 1
0 3 1
3 3 1
3 4 2
So for these data I would like to get:
TimeOfDay | CategoryId
0 | 1 or 3 numberOfSalesInCategory for both is 1
1 | 2 numberOfSalesInCategory is 3
2 | 2 only one category
3 | 4 numberOfSalesInCategory is 2
Technically, you are looking for the mode. There can be multiple modes, if multiple values all have the same frequency. If you are happy to arbitrarily choose one, then a conditional aggregation with row_number() is the solution:
select TimeOfDay,
max(case when seqnum = 1 then CategoryId end) as ModeCategory
from (select t.TimeOfDay, a.CategoryId, count(*) as numberOfSalesInCategory,
row_number() over (partition by t.TimeOfDay order by count(*) ) as seqnum
from Auction a join
Time t
on t.id = a.ClosedTime
where a.isSuccess = 1
group by t.TimeOfDay, a.CategoryId
) ta
group by TimeOfDay;
You could put the current statement in a CTE, rank them with RANK() and then do a stuff statement.
e.g.
; WITH T AS (SELECT t.TimeOfDay, a.CategoryId, COUNT(a.CategoryId)
numberOfSalesInCategory
FROM Auction a
JOIN Time t ON t.Id = a.ClosedTime
WHERE IsSuccess = 1
GROUP BY t.TimeOfDay, a.CategoryId)
, S AS (SELECT T.*
, RANK() OVER (PARTITION BY TimeOfDay ORDER BY numberOfSalesInCategory DESC) RankOrder
FROM T)
SELECT DISTINCT TimeOfDay
, STUFF(((SELECT ' or ' + CONVERT(NVARCHAR, CategoryId)
FROM S
WHERE RankOrder = 1
AND TimeOfDay = BloobleBlah.TimeOfDay
FOR XML PATH('')), 1, 4, '') CategoryId
FROM S BloobleBlah

Query Two Different Table that have same field item

I have two table
Table_A
ID PostId Item Stock Price
1 1 A 30 10
2 1 B 40 20
3 2 A 50 5
4 3 A 50 25
Table_B
ID PostId Item_ID Sold Price
1 1 1 2 20
2 1 2 2 40
3 1 1 1 10
4 2 3 3 15
5 2 3 1 5
I want to queries from above two table that have same 'PostID' and COUNT and SUM some field group by 'PostID', expected output would be like this
Output
ID PostId Total Item Total Stock Total Buyer(s) Total Sold Total Price
1 1 2 70 3 5 70
I've try to JOIN it, but result still miss calculate
SELECT Table_A.PostId AS PostId, COUNT(Table_A.Item) AS Total_Item, SUM(Table_A.stock) AS Total_Stock, COUNT(Table_B.Item_ID) AS total_buyer, SUM( Table_B.Sold ) AS TotalSold, SUM( Table_B.Price ) AS Total_Price
FROM Table_A
LEFT JOIN Table_B
ON Table_A.PostId = Table_B.PostId
WHERE Table_A.PostId = '1'
GROUP BY Table_A.PostId
LIMIT 0 , 30
Any suggestion for this query problem?? Thank you
SELECT Table_B.PostId AS PostId,
MIN(Table_A.Total_Item) AS Total_Item,
MIN(Table_A.Total_Stock) AS Total_Stock,
COUNT(Table_B.Item_ID) AS total_buyer,
SUM( Table_B.Sold ) AS TotalSold,
SUM( Table_B.Price ) AS Total_Price
FROM Table_B
LEFT JOIN
(
SELECT PostId,
COUNT(Item) AS Total_Item,
SUM(stock) AS Total_Stock
FROM
Table_A
GROUP BY PostId
) Table_A
ON Table_B.PostId=Table_A.PostId
WHERE Table_B.PostId = '1'
GROUP BY Table_B.PostId
LIMIT 0 , 30