SQL MULTIPLE JOIN-LEFT OUTER JOIN [Find Values between date range] - sql-server

I have 4 tables to use JOIN query in MSSQL
1. Sales: ItemID, AreaCode, IndID,Cost
2. Ind: AreaCode, IndID, Insite,InLocation
3. ItemPrice: ItemID, AreaID, IndID, ActicationDate, Price
4. Invoice: ItemID, IndID, AreaId, InvoiceDate
I want to get IndID and AreaCode from table 1 on basis of that I want to find records from Ind table and from those records look for Insite and AreaCode with InLocation=’ ’ from same table.
From that records get IndID .
Using that IndID and ItemID, find that Price for that particular Item from ItemPrice Table. The price for that particular item is from the activationDate is applicable to specific invoice on basis of date.
I.e. If Activation Date is 1st Jan and Price is $5 -->And Invoice date is 2nd then Price is $5. But if next day ActivationDate is 2nd Jan and Price is updated to $7. and Invoice is created on 3rd Jan the price should be $7.
I.e.
Getting this:
Looking for something like below
SELECT DISTINCT sl.AreaCode,sl.IndID,sl.cost,
id.IndID, id.AreaCode, id.InLocation, id.InSite,
id2.IndID, id2.AreaCode, id2.InLocation, id2.InSite,
ip.ItemId,ip.price,ip.ActivationDate,
iv.InvoiceDate
from Sales s
LEFT OUTER JOIN Ind id
ON s.IndID= id.IndID
AND s.AreaCode=id.AreaCode
LEFT OUTER JOIN Ind id2
ON id.AreaCode=id2.AreaCode
AND id.IndID=id2.IndID
AND id.InSite=id2.InSite
AND id.InLocation = ''
LEFT OUTER JOIN ItemPrice ip
ON s.ItemId=ip.ItemId
AND id2.AreaCode=ip.AreaCode
AND id2.IndID=ip.IndID
LEFT OUTER JOIN Invoice iv
ON s.ItemId=iv.ItemId
AND iv.InvoiceDate >= ip.ActivationDate

Finally cracked.
select *,
(select top 1 price
from itemPrice
where itemPrice.ActivationDate <= t.InvoiceDate
order by itemPrice.ActivationDate desc) as Price
from Invoice t
Here s result

Related

SQL - Return first non-empty value for previous days

I'm currently working with an exchange rates table in SQL that has these fields:
| Country | ExchangeRateDt | ExchangeRateValue |
| DK | 202000601 | 0.2 |
| DK | 202000603 | 0.21 |
| HR | 202000601 | 0.10 |
| HR | 202000602 | 0.12 |
For each currency I don't have a value for any day of the year because of bank holidays or simply weekends.
I need to join it with an order table where some orders are placed on weekends and on a specific day I could not have an exchange rate to calculate taxes.
I need to take the first non missing value from the previous days (so in the examples should I have an order for day 2020-06-02 in Denmark I should exchange it using the rate 0.2)
I thought about using a calendar table but I can't manage to get the job done.
Can someone help me?
Thanks in advance,
R
To get the most recent value less than or equal to the current day:
SELECT
<whatever columns you need from order>
,exchange.ExchangeRateValue
FROM
<order table> order
LEFT JOIN
<exchange rate table> exchange
ON exchange.Country = order.Country
AND exchange.ExchangeRateDt =
(
SELECT
MAX(ExchangeRateDt)
FROM
<exchange rate table>
WHERE
Country = order.Country
AND ExchangeRateDt <= order.OrderDt
)
Ensure the clustered index on the exchange rate table is (Country, ExchangeRateDt).
I have this as a left join so you will still return order results if the currency information is somehow missing. You would have to refer to business rules on how to proceed if no exchange rate was available.
You would typically create a calendar table that stores all the days you are interested in, say dates, with each date on a separate row.
You would also probably have a table that lists the countries: I assumed countries.
Then, one option is a lateral join:
select c.country, d.date, t.ExchangeRateValue
from dates d
cross join countries c
outer apply (
select top (1) t.*
from mytable t
where t.country = c.country and t.ExchangeRateDt <= d.date
order by t.ExchangeRateDt desc limit 1
) t
If you don't have these two tables, or can't create them, then one option is a recursive query to generate the dates and a subquery to list the countries. For example, this would generate the data for the month of June:
with dates as (
select '20200601' date
union all
select dateadd(day, 1, date) from dates where date < '20200701'
)
select c.country, d.date, t.ExchangeRateValue
from dates d
cross join (select distinct country from mytable) c
outer apply (
select top (1) t.*
from mytable t
where t.country = c.country and t.ExchangeRateDt <= d.date
order by t.ExchangeRateDt desc limit 1
) t
You should be able to do the mapping between the transation date and the exchange rate date with this query:
select TAB.primary_key, TAB.TransationDate, max(EXR.ExchangeRateDt)
from yourtable TAB
inner join exchangerate EXR
on TAB.Country = EXR.Country and TAB.TransationDate >= EXR.ExchangeRateDt
group by TAB.primary_key, TAB.TransationDate

How to Combine Three table with aggregate functions in Sql Server

I have three tables one is SaleinfoTB second table is StarReadingEndReading and 3rd table is ExpenseinfoTb in the SaleinfoTB table i want to sum TotalBill Column and StartReadingEndReading table i want to sum ActualSell Column and Plus both Sum and mins from ExpenseintoTB Table Column Amount
select SUM((SaleinfoTB.TotalBill)+ SUM(StartReadingEndReading.ActualSell)) -
(SUM(ExpenseinfoTB.Amount)) from SaleinfoTB,SaleinfoTB,ExpenseinfoTB
where SaleinfoTB.Date=StartReadingEndReading.ReadingDate
and ExpenseinfoTB.ExpenseDate=SaleinfoTB.Date
and ExpenseinfoTB.ExpenseDate=StartReadingEndReading.ReadingDate
I think what you are getting at is how to get the sum of each item before you do the addition/subtraction
SELECT (ISNULL(SaleinfoTB.SumTotalBill,0)
+ ISNULL(StartReadingEndReading.SumActualSell,0))
- ISNULL(ExpenseinfoTB.SumAmount,0)
FROM
(SELECT SUM(TotalBill) SumTotalBill, [Date]
FROM SaleinfoTB
Group By [Date])SaleinfoTB
INNER JOIN
(SELECT SUM(ActualSell) SumActualSell, ReadingDate
FROM StartReadingEndReading
Group By ReadingDate)StartReadingEndReading
ON SaleinfoTB.[Date]=StartReadingEndReading.ReadingDate
INNER JOIN
(SELECT SUM(Amount) SumAmount, ExpenseDate
FROM ExpenseinfoTB
Group By ExpenseDate)ExpenseinfoTB
ON ExpenseinfoTB.ExpenseDate=SaleinfoTB.[Date]

How to query a Master database using Inner Join links to 2 sub-databases that are identical to each other

I have an Inventory table containing Master file info and 2 Movement History tables (Current Year and Last Year).
I want to use a Query to extract Movements from (say) June LAST Year to March THIS Year in Code, Date sequence.
I am relatively new to SQL and have tried to use the following INNER JOIN structure to do this:
SELECT Code, Descrip, Category, MLast.Date, MLast.DocNo, MCurr.Date, MCurr.DocNo
FROM Stock AS S
INNER JOIN MoveTrnArc MLast ON MLast.Stockcode = S.Code
AND MLast.Date >='2011/06/01' AND MLast.Date <='2012/03/31'
INNER JOIN MoveTrn MCurr ON MCurr.Stockcode = S.Code
AND MCurr.Date >='2011/06/01' AND MCurr.Date <='2012/03/31'
ORDER BY S.Code
This creates a Query Table with the following column structure:
Code | Descrip | Category | Date | DocNo | Date | DocNo |
...where the data from the LAST Year table appears in the first Date/DocNo columns and the CURRENT Year data appears in the second Date/DocNo columns.
What must I do to the Query to have each Movement in its own row or is there a better, more efficient Query to achieve this?
Also, I need the Movements listed in Code followed by Date sequence.
use union all instead of joins
select s.Code , s.Descrip , s.Category , t.Date , t.DocNo
from
(
select Stockcode, Date, DocNo from MoveTrnArc
union all
select Stockcode, Date, DocNo from MoveTrn
) t join Stock s on s.Code = t.Stockcode
where t.Date >='2011/06/01' AND t.Date <='2012/03/31'
beside careful with comparing dates, if Date column is type datetime and includes time you have to change t.Date <='2012/03/31' into t.Date <'2012/04/01' to include all the rows from 31st of march,
as '2012/03/31' is casted as '2012/03/31 00:00:00.000'

Check for applicable Group query for shopping cart

I have problem for one of discount check condition. I have tables structure as below:
Cart table (id, customerid, productid)
Group table (groupid, groupname, discountamount)
Group Products table (groupproductid, groupid, productid)
While placing an order, there will be multiple items in cart, I want to check those items with top most group if that group consists of all product shopping cart have?
Example:
If group 1 consists 2 products and those two products exists in cart table then group 1 discount should be returned.
please help
It's tricky, without having real table definitions nor sample data. So I've made some up:
create table Carts(
id int,
customerid int,
productid int
)
create table Groups(
groupid int,
groupname int,
discountamount int
)
create table GroupProducts(
groupproductid int,
groupid int,
productid int
)
insert into Carts (id,customerid,productid) values
(1,1,1),
(2,1,2),
(3,1,4),
(4,2,2),
(5,2,3)
insert into Groups (groupid,groupname,discountamount) values
(1,1,10),
(2,2,15),
(3,3,20)
insert into GroupProducts (groupproductid,groupid,productid) values
(1,1,1),
(2,1,5),
(3,2,2),
(4,2,4),
(5,3,2),
(6,3,3)
;With MatchedProducts as (
select
c.customerid,gp.groupid,COUNT(*) as Cnt
from
Carts c
inner join
GroupProducts gp
on
c.productid = gp.productid
group by
c.customerid,gp.groupid
), GroupSizes as (
select groupid,COUNT(*) as Cnt from GroupProducts group by groupid
), MatchingGroups as (
select
mp.*
from
MatchedProducts mp
inner join
GroupSizes gs
on
mp.groupid = gs.groupid and
mp.Cnt = gs.Cnt
)
select * from MatchingGroups
Which produces this result:
customerid groupid Cnt
----------- ----------- -----------
1 2 2
2 3 2
What we're doing here is called "relational division" - if you want to search elsewhere for that term. In my current results, each customer only matches one group - if there are multiple matches, we need some tie-breaking conditions to determine which group to report. I prompted with two suggestions in comments (lowest groupid or highest discountamount). Your response of "added earlier" doesn't help - we don't have a column which contains the addition dates of groups. Rows have no inherent ordering in SQL.
We would do the tie-breaking in the definition of MatchingGroups and the final select:
MatchingGroups as (
select
mp.*,
ROW_NUMBER() OVER (PARTITION BY mp.customerid ORDER BY /*Tie break criteria here */) as rn
from
MatchedProducts mp
inner join
GroupSizes gs
on
mp.groupid = gs.groupid and
mp.Cnt = gs.Cnt
)
select * from MatchingGroups where rn = 1

Distinct with Count and SQl Server 2005

Trying to work on a query that will return the top 3 selling products with the three having a distinct artist. Im getting stuck on getting the unique artist.
Simplified Table schema
Product
ProductID
Product Name
Artist Name
OrderItem
ProductID
Qty
So results would look like this...
PID artist qty
34432, 'Jimi Hendrix', 6543
54833, 'stevie ray vaughan' 2344
12344, 'carrie underwood', 1
Use this:
with summed_sales_of_each_product as
(
select p.artist_name, p.product_id, sum(i.qty) as total
from product p join order_item i
on i.product_id = p.product_id
group by p.artist_name, p.product_id
),
each_artist_top_selling_product as
(
select x_in.artist_name, x_in.product_id, x_in.total
from summed_sales_of_each_product x_in where total =
(select max(x_out.total)
from summed_sales_of_each_product x_out
where x_out.artist_name = x_in.artist_name)
)
select top 3
artist_name, product_id, total
from each_artist_top_selling_product
order by total desc
But you cannot stop at that query, how about if there are two products on one artist that are ties on highest selling? This is how the data like this...
beatles yesterday 1000
beatles something 1000
elvis jailbreak rock 800
nirvana lithium 600
tomjones sexbomb 400
...will result to following using the above query:
beatles yesterday 1000
beatles something 1000
elvis jailbreak rock 800
Which one to choose? yesterday or something? Since you cannot arbitrarily chose one over the other, you must list both. Also, what if the top 10 highest selling belongs to beatles and are ties, each with a quantity of 1000? Since that is the very best thing you are avoiding(i.e. reporting same artist on top 3), you have to amend the query so the top 3 report will look like this:
beatles yesterday 1000
beatles something 1000
elvis jailbreak rock 800
nirvana lithium 600
To Amend:
with summed_sales_of_each_product as
(
select p.artist_name, p.product_id, sum(i.qty) as total
from product p join order_item i
on i.product_id = p.product_id
group by p.artist_name, p.product_id
),
each_artist_top_selling_product as
(
select x_in.artist_name, x_in.product_id, x_in.total
from summed_sales_of_each_product x_in
where x_in.total =
(select max(x_out.total)
from summed_sales_of_each_product x_out
where x_out.artist_name = x_in.artist_name)
),
top_3_total as
(
select distinct top 3 total
from each_artist_top_selling_product
order by total desc
)
select artist_name, product_id, total
from each_artist_top_selling_product
where total in (select total from top_3_total)
order by total desc
How about if the beatles has another product which has 900 qty? Will the above query still work? Yes, it will still work. Since the top_3 CTE only concerns itself from the already filtered top qty on each artist. So this source data...
beatles yesterday 1000
beatles something 1000
beatles and i love her 900
elvis jailbreak rock 800
nirvana lithium 600
tomjones sexbomb 400
...will still result to following:
beatles yesterday 1000
beatles something 1000
elvis jailbreak rock 800
nirvana lithium 600
If I have understood your schema correctly, you should be able to do it like this:
select top 3 * from(
select p.ProductId, p.ArtistName, sum(o.qty) as qty from Product p, OrderItem o
where p.ProductId = o.ProductId
group by p.productId, p.ArtistName
order by sum(o.qty)
)
I don't know what you want to do if an Artist has two top-ranked products with identical sales--this will return two in case of a tie.
If you want to add another criteria, such as "most recent", you have to add that to both subqueries.
select top 3 sales_by_item.ProductID,
sales_by_item.Artist,
sales_by_item.Qty
from
(
select * from product x
inner join OrderItem y
on x.productid = y.productid
group by productid, Artist
) sales_by_item
inner join
(
select artist, max(qty) as maxqty
from product x
inner join OrderItem y
on x.productid = y.productid
group by artist
) max_by_artist
on sales_by_item.artist = max_by_artist.artist
and sales_by_item.qty = max_by_artist.maxqty
order by sales_by_item.qty
Edited to make subquery names more descriptive
Second attempt. I’m not in a position to test this code, and I’m not sure if I’ve got that “partition by” clause configured correctly. The idea is:
The inner query gets the sum of Qty for all product/artists, and uses the row_number() function to number them starting with the largest, and resets the ordering for each artist. (This can be done, but my syntax may be off.)
The outer query picks out the first (largest) item for each artist, and returns only the first three (ordere by Qty)
If an artists top two products tie for total Qty, I arbitrarily break the tie in favor of the “earliest” album.
(I try to avoid using "Top n", but it's late and I don't want to tackle another row_number() function.)
SELECT top 3
ProductId
,ArtistName
,Qty
from (-- Products + Artists by total qty
select
pr.ProductId
,pr.ArtistName
,sum(oi.Qty) Qty
,row_number() over (partition by pr.ArtistName order by pr.ArtistName, sum(oi.Qty) desc, pr.ProductId) Ranking
from Product pr
inner join OrderItem oi
on oi.ProductID = pr.ProductID
group by pr.ProductId, pr.ArtistName) BestSellers
where Ranking = 1
group by ProductId, ArtistName) BestArtists
order by Qty desc
Analyzing your request, it sounds like the results should be the highest product quantity for the top three artists. So, if Jimi Hendrix has the top 10 product quantities and Stevie Ray Vaughan is 11th, you want Jimi with his highest product then Stevie with his highest product.
With ProductRanksForArtists As
(
Select P.ProductId, P.ArtistName, Sum(O.Qty) As Total
, ROW_NUMBER OVER( PARTITION BY P.ArtistName ORDER BY Sum(O.Qty) DESC ) As ProductRank
From Product As P
Join OrderItem As O
On O.ProductId = P.ProductId
Group By P.ProductId, P.ArtistName
)
, HighestProductForArtists As
(
Select ProductId, ArtistName, Total
, ROW_NUMBER OVER( ORDER BY Total DESC ) As TotalRank
From ProductRanksForArtists
Where ProductRank = 1
)
Select ProductId, ArtistName, Total
From HighestProductForArtists
Where TotalRank <= 3
Try this
Select top 3 artist, count(artist) from tablename group by artist order by artist count(artist) desc

Resources