Frage

Entschuldigung für den schrecklichen Titel. Ich konnte mich nicht besser einfallen lassen, um das Problem kurz zu beschreiben.

Hier ist das Szenario:

Benutzer können Serviceanweisungen einrichten, die an einer Arbeitsauftrag basieren, die auf dem Kunden der Bestellung, dem Kredittyp, der Bank, der Preisregion und Service der Bestellung basiert. Die Serviceanweisung wird mit den folgenden Parametern eingerichtet:

  1. Gilt für alle Kunden oder einen bestimmten Kunden
  2. Gilt für alle Kreditarten oder einen bestimmten Kredittyp
  3. Gilt für alle Banken oder eine bestimmte Bank
  4. Gilt für alle Preisregionen oder eine bestimmte Preisregion
  5. Gilt für einen bestimmten Dienst

Ein Wert von 1 in einer Spalte in der nachstehend beschriebenen Service_instruction -Tabelle entspricht "All".

Die Tabellenstruktur (die relevanten Teile sowieso):

order (order_num INT PRIMARY KEY, client_num INT, loan_type INT, 
       bank_num INT, pricing_region INT)

work_order_line(work_order_line_num INT PRIMARY KEY, order_num INT, service_num INT,
                description TEXT)

service_instruction(instruction_num INT PRIMARY KEY, service_num INT,
                    service_description TEXT, client_num INT NULL,
                    bank_num INT NULL, region_num INT, loan_type_cd TINYINT) 

Ich muss ein Skript schreiben, das den spezifischsten Anweisungen für einen Dienst auswählt, falls vorhanden. Zum Beispiel:

Der Benutzer hat die folgenden Serviceanweisungen eingerichtet:

  1. (Alle Kunden), (alle Kreditarten), (alle Banken), (alle Preisregionen), (Service A), Anweisungen: Arbeit A.
  2. (Kunde 1), (alle Kreditarten), (alle Banken), (Region 3), (Service A), Anweisungen: Arbeit b

Eine Bestellung, die für Client 1, Region 3 und Service A eintritt, sollte "Arbeit b" angehängt haben. Eine Bestellung, die für Client 2, Region 3 und Service A eintritt, sollte "Arbeit ein" angehängt haben. Eine Bestellung, die für Service B kommt, sollte nichts angehängt haben. Usw.

Derzeit erfolgt dies in Code, aber die Umstände erfordern das Scripting. Gibt es eine Möglichkeit, dies zu tun, das eleganter ist (und vielleicht bessere Leistung) als das aktuelle Chaos von if ... Else und Isnulls, an dem ich arbeite?

Bearbeiten: Hier sind einige Beispieldaten.

service_instruction:

instruction_num | service_num | instruction_desc | bank_num | region_num | loan_type_cd | client_num
       3               251    'Take the photos, yeah'  17        96            3             1
       4               251  'Bid for debris removal.'  1         471           1             7
       7               251  'Bid for debris removal.   1          3            1             1                                    
                                 Do not perform'    

bestellen]:

order_num | client_num | loan_type | bank_num | pricing_region
    1           3            1          1             3
    2           7            3          1            471
    3           2            3          17            96
    4           5            2          6             17

Work_order_line:

work_order_line_num | order_num | service_num | description
         20              1             251         NULL
         21              2             251         NULL
         22              3             251         NULL
         26              4             251         NULL

Das Endergebnis wäre:

Work_order_line:

work_order_line_num | order_num | service_num | description
         20              1             251    'Bid for debris removal. Do not perform.'
         21              2             251    'Bid for debris removal.'
         22              3             251    'Take the photos, yeah'
         26              4             251    'Bid for debris removal. Do not perform.'

Ein Wert von 1 für client_num, bank_num oder links_type_cd in service_instruction bedeutet, dass er für alle Kunden, Banken oder Kredittypen gilt. Um die Dinge weiter zu komplizieren, hängt die PREISBILDUNG IN SERVICE_Instruction vom Client ab. Wenn beispielsweise das Client_num 1 ist ("alle Clients"), zeigt ein Region_Num von 3 "alle Regionen" an. Wenn das Client_Num 3 ist, ist Region_Num 39 "alle Regionen". Ich bin nicht wirklich auf der Suche nach jemandem, der das Skript für mich aufschreibt, eher einen Zeiger in die richtige konzeptionelle Richtung, so dass die Komplikation wahrscheinlich vorerst ignoriert werden kann.

Hier ist, woran ich gearbeitet habe, um dies zu lösen und etwas für Kürze zu bearbeiten. Ja, es gibt einen Cursor. Ja, es wird in eine Produktionsumgebung versetzt. Nein, ich bin nicht glücklich darüber. Deshalb bin ich hierher gekommen!

--Table var. containing orders we need to update
DECLARE @order_numbers TABLE (order_num INT PRIMARY KEY
    , client_num SMALLINT NOT NULL
    , order_type_cd TINYINT NOT NULL
    , loan_type_cd TINYINT NOT NULL
    , bank_num TINYINT NOT NULL
    , pricing_region INT NOT NULL
    , service_num INT NOT NULL
    , service_instruction_num SMALLINT NULL);

--Table containing the match data
DECLARE @instruction_matches TABLE (instruction_num SMALLINT PRIMARY KEY
        , client_match TINYINT
    , loan_type_match BIT
    , bank_match BIT
    , region_match BIT
    , total_matches TINYINT);

DECLARE @best_instruction SMALLINT
    , @order_num INT
    , @client_num SMALLINT
    , @loan_type_cd TINYINT
    , @bank_num TINYINT
    , @region_num INT
    , @service_num INT;

--Get the orders to update
INSERT INTO @order_numbers (order_num
    , client_num
    , order_type_cd
    , loan_type_cd
    , bank_num
    , pricing_region
    , service_num)
SELECT o.order_num, oc.client_num, o.order_type_cd, oc.loan_type_cd, oc.bank_num, oc.pricing_region, wol.service_num 
FROM [order] o
    inner join order_context oc ON o.context_num = oc.context_num
    inner join work_order_line wol ON o.order_num = wol.order_num
WHERE o.status_cd = 1 AND o.exchange_status_cd = 2 AND o.received_from_hub_ind = 1
    AND o.viewed_dt IS NULL

DECLARE o_cursor CURSOR LOCAL
FOR SELECT order_num FROM @order_numbers;
OPEN o_cursor;
FETCH NEXT FROM o_cursor
INTO @order_num;

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @service_num = (SELECT service_num FROM @order_numbers WHERE order_num = @order_num);
    SET @client_num = (SELECT client_num FROM @order_numbers WHERE order_num = @order_num);
    SET @loan_type_cd = (SELECT loan_type_cd FROM @order_numbers WHERE order_num = @order_num);
    SET @bank_num = (SELECT bank_num FROM @order_numbers WHERE order_num = @order_num);
    SET @region_num = (SELECT pricing_region FROM @order_numbers WHERE order_num = @order_num);

    INSERT INTO @instruction_matches (instruction_num, client_match, loan_type_match, bank_match, region_match)
    SELECT
        instruction_num
        , CASE 
            WHEN client_num = @client_num OR client_num = 1
                THEN 1
            ELSE 0
          END
        , CASE
            WHEN loan_type_cd = @loan_type_cd OR loan_type_cd = 1
                THEN 1
            ELSE 0
          END
        , CASE
            WHEN bank_num = @bank_num OR bank_num = 1
                THEN 1
            ELSE 0
          END
        , CASE -- region_num = 4 means INSPECTIONS ONLY. will need to be re-written for pres.
            WHEN region_num = @region_num
                OR (client_num != 1 AND region_num = (SELECT region_num FROM region 
                                                      WHERE client_num = @client_num
                                                        AND region_type_cd = 2 
                                                        AND order_item_type_cd = 2
                                                        AND region_id = 1)
                )
                THEN 2
            WHEN client_num = 1 AND region_num = 4
                THEN 1
            ELSE 0
          END
    FROM service_instruction
    WHERE service_num = @service_num;

    UPDATE @instruction_matches
    SET total_matches = (client_match + loan_type_match + bank_match + region_match);

    SET @best_instruction = (SELECT TOP(1) instruction_num FROM @instruction_matches ORDER BY total_matches DESC);

    UPDATE @order_numbers
    SET service_instruction_num = @best_instruction 
    WHERE order_num = @order_num;

    FETCH NEXT FROM o_cursor
    INTO @order_num;
END;
War es hilfreich?

Lösung

Mein erster Schritt wäre, die zu bewerten service_instruction nach Spezifität/Allgemeinheit - Ich glaube nicht 1S und Krawatten werden gebrochen von: bank_num ist spezifischer als loan_type_cd ist spezifischer als client_num:

select *, row_number() over ( order by case when bank_num=1 then 1 else 0 end+
                                       case when loan_type_cd=1 then 1 else 0 end+
                                       case when client_num=1 then 1 else 0 end,
                                     case when bank_num=1 then 1 else 0 end,
                                     case when loan_type_cd=1 then 1 else 0 end,
                                     case when client_num=1 then 1 else 0 end ) as gen
from service_instruction;

produziert:

instruction_num service_num instruction_desc    bank_num    region_num  loan_type_cd    client_num  gen
3   251 Take the photos, yeah   17  96  3   1   1
4   251 Bid for debris removal. 1   471 1   7   2
7   251 Bid for debris removal. Do not perform  1   3   1   1   3

Dies kann dann verbunden werden order und work_order_line und für die Zeile für jeden gefiltert work_order_line_num mit der niedrigsten Allgemeinheit (gen), vielleicht so etwas:

with w as (
    select *, row_number() over ( order by case when bank_num=1 then 1 else 0 end+
                                           case when loan_type_cd=1 then 1 else 0 end+
                                           case when client_num=1 then 1 else 0 end,
                                         case when bank_num=1 then 1 else 0 end,
                                         case when loan_type_cd=1 then 1 else 0 end,
                                         case when client_num=1 then 1 else 0 end ) as gen
    from service_instruction )
select *
from( select l.*, instruction_desc, row_number() over ( partition by l.work_order_line_num, 
                                                                     l.order_num, 
                                                                     l.service_num
                                                        order by gen ) as gen_rank
      from work_order_line l join [order] o on(o.order_num=l.order_num)
           join w on( l.service_num=w.service_num 
                      and (o.bank_num=w.bank_num or w.bank_num=1) 
                      and (o.loan_type_cd=w.loan_type_cd or w.loan_type_cd=1)
                      and (o.client_num=w.client_num or w.client_num=1)) ) z
where gen_rank=1

was produziert:

work_order_line_num order_num   service_num instruction_desc    gen_rank
20  1   251 Bid for debris removal. Do not perform  1
21  2   251 Bid for debris removal. 1
22  3   251 Take the photos, yeah   1
26  4   251 Bid for debris removal. Do not perform  1

Anmerkungen:

  1. Ich habe angenommen, dass Sie sich partitionieren müssen work_order_line_num, order_num und service_num Aber vielleicht sind nicht alle je nach PK von notwendig work_order_line
  2. Ich habe das ignoriert pricing_region Die Komplexität, die Sie erwähnen, aber nicht vollständig angeben - hoffentlich können Sie sie auf ähnliche Weise wie die anderen in die Abfrage einarbeiten
  3. Sie müssen den CTE wahrscheinlich mit Ihren Regeln für Spezifität/Allgemeinheit beheben
  4. Ich habe auf 2008R2 - YMMV getestet, wenn Sie in einer früheren Version sind
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit dba.stackexchange
scroll top