I'm working on a project where I need to update a Mailing Rate column that is currently blank. I have a few different tables of rates/costs based on type of delivery service. Within each table is essentially a table of weights and zones where the cost depends on weight (y column) and distance travelled (x column).
The table I need to update lists the various types of delivery service, associated weight and associated distance travelled for each piece of mail. I'm having trouble figuring out the logic on how to write a query to update the rates column in this table that match the corresponding tables' lookup of weight/travel distance for each service.
Below is an example of the tables:
| Type of Service | Weight | Distance | Cost |
+-----------------+----------+----------+----------+
| A | 1 | 15 | ? |
| B | 2 | 20 | ? |
| C | 3 | 10 | ? |
| | | | |
| Service A Table | | | |
| Weight | 10 km | 15 km | 20 km |
| 1 | $25.00 | $30.00 | $40.00 |
| 2 | $27.00 | $32.00 | $41.00 |
| 3 | $28.00 | $34.00 | $43.00 |
| | | | |
| Service B Table | | | |
| Weight | 10 km | 15 km | 20 km |
| 1 | $28.00 | $32.00 | $41.00 |
| 2 | $29.00 | $35.00 | $44.00 |
| 3 | $30.00 | $37.00 | $47.00 |
+-----------------+----------+----------+----------+
You can use a MERGE
statement between your main table you want to calculate the cost for and the services table. Do the merge on weight and distance and calculate the costs based on service type.
Consult the merge documentation it should be very easy to do it. I could do it on here but better learning for you.
Here there is an example: https://www.sqlservertutorial.net/sql-server-basics/sql-server-merge/
You can use left join
to bring in all the tables . . . and a lot of case
logic to choose the columns:
update t
set cost = (case when distance = '10 km'
then coalesce(sa.10km, sb.10km, sc.10km)
when distance = '15 km'
then coalesce(sa.15km, sb.15km, sc.15km)
when distance = '20 km'
then coalesce(sa.20km, sb.20km, sc.20km)
end)
from t left join
servicea sa
on sa.weight = t.weight and t.service = 'A' left join
servicea sb
on sb.weight = t.weight and t.service = 'B' left join
servicea sc
on sc.weight = t.weight and t.service = 'C' ;
Since you have different tables for each type of service it is going to be hard for you if you add new types of service. Optimally the type of service would be a column in one table for the costs. You could create views for each type of service if for some reason you want to view your data that way. I would organize the data like this for easy lookup:
create table ServiceCosts(
ServiceType char(1),
MaxDistance decimal(9, 2),
Cost decimal(9, 2),
constraint pk_ServiceCosts primary key clustered (ServiceType, MaxDistance)
)
Then you could update the table using an outer apply:
update s
set s.Cost = oa.Cost
from SourceTable s
outer apply (
select top(1) Cost
from ServiceCosts sc
where sc.Weight >= s.Weight
and sc.Distance >= s.Distance
order by Cost -- cheapest that fits under weight and distance
) oa
There are a few options you can use, which option is best would depend on the exact criteria of your data. Does every table have every weight? Every distance? What do you do if there is no record meeting your criteria (Cost would be null here)?
Another option would be to create a function...
create function GetCost(
@ServiceType char(1),
@Weight decimal(9, 2),
@Distance decimal(9, 2)
) returns decimal(9, 2)
as
begin
declare @Cost decimal(9, 2) = null;
if @ServiceType = 'A'
select top(1) @Cost = case
when @Distance < 10 then [10 km]
when @Distance < 20 then [20 km]
when @Distance < 30 then [30 km]
else null
end
from ServiceATable
where Weight >= @Weight order by Weight -- least weight at or above limit
else if -- repeat for each service table
return @Cost;
end
Then update:
update ItemTable
set Cost = GetCost(ServiceType, Weigth, Distance)
where Cost is null; -- only update rows without Cost already
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.