在 sql 服务器中从 SELECT 插入或更新 table

INSERT or UPDATE the table from SELECT in sql server

我有一个要求,我必须检查营业日期的记录是否已经存在于 table 中,然后我需要从 select 语句中更新该营业日期的值,否则我必须从 select 语句中插入该营业日期。以下是我目前只插入的完整查询:

    INSERT INTO 
         gstl_calculated_daily_fee(business_date,fee_type,fee_total,range_id,total_band_count) 
     select
    @tlf_business_date,
    'FEE_LOCAL_CARD',
     SUM(C.settlement_fees),
     C.range_id,
     Count(1)
From
(
    select
        *
    from
        (
            select
                rowNumber = @previous_mada_switch_fee_volume_based_count + (ROW_NUMBER() OVER(PARTITION BY DATEPART(MONTH, x_datetime) ORDER BY x_datetime)),
                tt.x_datetime
            from gstl_trans_temp tt where (message_type_mapping = '0220') and card_type ='GEIDP1' and response_code IN('00','10','11')  and tran_amount_req >= 5000 AND merchant_type NOT IN(5542,5541,4829)
         
        ) A

        CROSS APPLY
            (
                select
                     rtt.settlement_fees,
                     rtt.range_id
                     From gstl_mada_local_switch_fee_volume_based rtt
                    where A.rowNumber >= rtt.range_start
                        AND (A.rowNumber <= rtt.range_end OR rtt.range_end IS NULL)
            ) B 
) C
group by CAST(C.x_datetime AS DATE),C.range_id

我尝试使用 if exists 但无法满足上述完整查询。

if exists (select 
                business_date  
          from gstl_calculated_daily_fee 
          where 
          business_date = @tlf_business_date)



UPDATE gstl_calculated_daily_fee
    SET fee_total = @total_mada_local_switch_fee_low
    WHERE fee_type = 'FEE_LOCAL_CARD' 
    AND business_date = @tlf_business_date 

    else

     INSERT INTO 
         

请帮忙。

您需要一个带连接的 MERGE 语句。

基本上,MERGE 的问题是我们只想合并目标 table 的一个子集。为此,我们将 table 作为 CTE 进行预过滤。我们还可以将源 table 作为 CTE。

在使用 CTE 时写 MERGE 时要非常小心。您必须确保将 CTE 中的目标完全过滤为要合并的行,然后使用 ON

匹配行
;with source as (
    select
        business_date = @tlf_business_date,
        fee_total = SUM(C.settlement_fees),
        C.range_id,
        total_band_count = Count(1)
    From
    (
            select
                rowNumber = @previous_mada_switch_fee_volume_based_count + (ROW_NUMBER() OVER(PARTITION BY DATEPART(MONTH, x_datetime) ORDER BY x_datetime)),
                tt.x_datetime
            from gstl_trans_temp tt where (message_type_mapping = '0220') and card_type ='GEIDP1' and response_code IN('00','10','11')  and tran_amount_req >= 5000 AND merchant_type NOT IN(5542,5541,4829)
         
    ) A

    CROSS APPLY
    (
            select
                 rtt.settlement_fees,
                 rtt.range_id
            From gstl_mada_local_switch_fee_volume_based rtt
            where A.rowNumber >= rtt.range_start
                AND (A.rowNumber <= rtt.range_end OR rtt.range_end IS NULL)
    ) B 
    group by CAST(A.x_datetime AS DATE), B.range_id
),

target as (
    select 
        business_date,fee_type,fee_total,range_id,total_band_count
    from gstl_calculated_daily_fee 
    where business_date = @tlf_business_date AND fee_type = 'FEE_LOCAL_CARD'
)

MERGE INTO target t
USING source s
ON t.business_date = s.business_date AND t.range_id = s.range_id

WHEN NOT MATCHED BY TARGET THEN INSERT
    (business_date,fee_type,fee_total,range_id,total_band_count) 
    VALUES
    (s.business_date,'FEE_LOCAL_CARD', s.fee_total, s.range_id, s.total_band_count)

WHEN MATCHED THEN UPDATE SET
    fee_total = @total_mada_local_switch_fee_low
;

MERGE 语句的工作方式是,它基本上在源和目标 table 之间执行 FULL JOIN,使用 ON 子句进行匹配。然后它将各种条件应用于生成的连接并根据它们执行语句。

您可以执行三种可能的条件:
WHEN MATCHED THEN
WHEN NOT MATCHED [BY TARGET] THEN
WHEN NOT MATCHED BY SOURCE THEN
以及三种可能的语句,都指向目标table:UPDATEINSERTDELETE(显然不是所有情况都适用)。

一个常见的问题是我们只想考虑目标的一个子集 table。对此有多种可能的解决方案:

我们可以过滤 WHEN MATCHED 子句中的匹配项,例如WHEN MATCHED AND target.somefilter = @somefilter。不过,这通常会导致完整的 table 扫描。

相反, 我们将过滤后的目标 table 放入 CTE 中,然后 MERGE 放入其中。 CTE 必须遵循 Updatable View 规则。我们还必须 select 我们希望插入或更新的所有列。但是我们必须确保我们完全过滤了目标,否则如果我们发出 DELETE 那么目标 table 中的所有行都将被删除。