简体   繁体   中英

Update an column in SQL Server with values from a different lookup table?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM