Count (*) для View, возвращающего разные результаты на SQL Server - PullRequest
0 голосов
/ 07 мая 2019

Я работаю над проблемой оптимизации ETL, которая требует создания временной таблицы, которая может быть объединена с финальной таблицей.В настоящее время у меня есть пара просмотров, которые используются для загрузки финального стола, и это занимает много времени.Я попытался взять логику SQL из представления и создал временную таблицу и заметил, что значения в временной таблице не соответствуют значениям в итоговой таблице.Чтобы заглянуть глубже, я несколько раз запускал count(*) в представлении и заметил, что результат для общего числа строк различен для каждого прогона примерно на 10/15 строк, которые дают или принимают.Представление содержит 16 столбцов из 9 таблиц, которые загружаются только один раз в день.Так что время, когда я запускаю count(*), базовые данные не меняются, но результат подсчета из представления меняется.

Это на сервере SQL Server 2016.Я попытался изучить логику просмотра, и ничто не выделяется как-то странно.Я попытался сделать count(*) для таблиц, которые загружают это представление, и количество таблиц не меняется.Я также попытался создать таблицу из 2 столбцов из логики представления, чтобы упростить проблему, и попробовал команду EXCEPT, которая по-прежнему дает около 20 строк несовместимых значений между таблицей из 2 столбцов, созданной из той же логики точного представления.

Вот воспроизведение определения VIEW с несогласованностью подсчета строк

USE [PROD]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE VIEW Base_View 
AS

select 
concat(x,   y,  z)feild1
,*
,ROW_NUMBER() OVER(PARTITION BY a,b ORDER BY some_Date) AS rec_num
,count(a) OVER(PARTITION BY a) AS rec_total
from (
    SELECT 
    case when RESULT='stored value' and e.code is not null then 'x' else '' end x
    ,case when RESULT='stored value 2' and r.l_id is not null then 'y' else '' end y
    ,case when RESULT in ('stored value 3','stored value 4') and t.amount is not null then 'z' else '' end z
    ,case when 
        CASE WHEN 
            (m.status = 'stored value 4' OR m.status = 'stored value 5') 
            AND m.bal < 0 
            THEN 
                CASE WHEN DATEDIFF(day,m.due,m.SNAP_DATE) < 0 
                    THEN 0 
                    ELSE DATEDIFF(day,m.due,m.SNAP_DATE)
                END 
            ELSE 0 
        END=0 AND w.W_ID is null AND m.status<>'stored value 5'
    then case 
        when RESULT in ('stored value 5','stored value 4')  
            then case when isnull(AMOUNT,0)<>0
                        then 'abc' 
                        else 'def' end
            else 'abc' end
    else 'def' 
    end imp_feild
    ,result
    ,es.emp_id
    ,concat(es.fname,' ',es.lname)task_emp
    ,concat(e.fname,' ',e.lname)ext_emp
    ,case when RESULT ='stored value' then t.P_STATUS else null end p_status
    ,t.CREATE_DATE
    ,t.l_key
    ,t.l_id
    ,m.status
    ,cast(w.wodate as date)wo_date
    ,rm.balance refi_balance,rnl.LOAN_key refi_loan,r.effective refi_effective
    ,case trancode when 'ext' then m.payment else null end ext_amount,e.entered ext_entered,e.effective ext_effective
      FROM 
       (
       select t0.*,ROW_NUMBER() OVER(PARTITION BY t0.some_KEY,cast(t0.CREATE_DATE as date),t0.output 
                                     ORDER BY t0.some_KEY,cast(t0.CREATE_DATE as date),t0.output ) AS SEQ_NUM
            from base_table_1 t0
            left join base_table_2 e0 
            on t0.c_e_key=e0.e_key
            where t0.active_rec_ind='Y' 
            and t0.output in (d,e,f,g) 
            and (t0.output2 in (j,k)
                or ISNULL(e0.some_KEY,'h') in ('u','w'))
            ) t
      join 
      base_table_3 l
      on t.loan_sf_id=l.loan_sf_id
      and t.active_rec_ind='Y'
      join base_table_4 m
      on 
      t.SOME_DATE=m.SNAP_DATE
      and   t.L_ID=m.L_ID
      left 
      join base_table_5 es
      on t.c_emp_key=es.emp_key
      left 
      join base_table_6 r
      on l.l_id=r.l_old_id
      and r.entered between dateadd(day,0,cast(t.CREATE_DATE as date)) and dateadd(day,0,t.SOME_DATE)
      left 
      join base_table_7 w
      on l.l_id=w.l_id
      and w.wodate between cast(t.CREATE_DATE_ETZ as date) and dateadd(day,0,t.SOME_DATE)
      left
      join base_table_8 wl
      on w.l_id=wl.l_id
      left
      join base_table_8 rnl
      on r.l_new_id=rnl.l_id
      left
      join base_table_8 rol
      on r.l_old_id=rol.l_id
      left 
      join base_table_4 rm
      on 
      dateadd(day,-1,r.effective)=rm.SNAP_DATE
      and   rol.L_ID=rm.L_ID  
      left 
      join 
       (select e0.*,ew.value_1,ew.new_key,ROW_NUMBER() OVER(PARTITION BY e0.L_ID,e0.ENT ORDER BY e0.L_ID,e0.ENT) AS SEQ_NUM
            from base_table_9 e0
              join base_table_5 ew 
              on e0.EMP_ID=ew.EMP_ID
            where e0.code='a'
            ) e
      on l.sid=e.sid
      and e.code='a' and RESULT='stored value 5'
      and e.entered between cast(t.CREATE_DATE as date) and dateadd(day,0,t.HOLD_DATE)
      AND e.SEQ_NUM=t.SEQ_NUM
      and ((isnumeric(e.roll_key)=1 and isnumeric(es.roll_key)=1  and e.roll_key=es.roll_key)
      or  ((isnumeric(e.roll_key)=0  or isnumeric(es.roll_key)=0) and e.FNAME+e.LNAME=es.FNAME+es.LNAME))
      where t.RESULT in ('abc','def')
      and cast(t.CREATE_DATE as date) between cast(dateadd(month,-12,getdate()) as date) and cast(getdate() as date)
      and (AGENT in ('lmn', 'pqr')
        or ISNULL(es.VKEY,'stored value 8') in ('xx','yy','zz'))
)x
where imp_feild='abc'
and concat(x,   y,  z)<>''
or imp_feild='def'

GO

Ожидаемый результат: он должен возвращать непротиворечивое число для подсчета строк и, как мы надеемся, должен устранить несоответствиепроблема значений в временной таблице.

Ответы [ 2 ]

0 голосов
/ 07 мая 2019

Это слишком долго для комментария.Использование * в представлении - очень плохая идея.Мало того, что представление НЕ обновляется (если вы не выполняете sp_refreshview), когда вы изменяете базовую таблицу, вы можете получить некоторые очень интересные вещи.

Проверьте это как пример того, насколько это может быть плохо.

create table ViewExample (Col1 int, Col2 int)
go

create view ViewExampleView as select * from ViewExample

go

insert ViewExample select 1, 2

go

select * from ViewExampleView --obviously we get just a single column

alter table ViewExample add Col3 int --add a new column to the table, surely the view will pick this up?

go

insert ViewExample select 3, 4, 5 --insert a new row with data in all three columns

go

select * from ViewExampleView --what??? The view says select * but we only get Col1 and Col2?

alter table ViewExample drop column Col2 --Oops we decide to drop this column because we don't need it anymore

select * from ViewExampleView --What in the world? Col2 doesn't exist in the table, why is it in the view? And what the heck is going on here. The data from Col3 is now moved to Col2

drop view ViewExampleView
drop table ViewExample

Обратите внимание, как при последнем выборе из представления, что данные из Col3 отображаются в Col2.Если это не убедит вас прекратить использование * в представлениях (и почти везде), я не знаю, что будет.

0 голосов
/ 07 мая 2019

Ваш запрос имеет between cast(dateadd(month,-12,getdate()) as date) and cast(getdate() as date) в нижней части. Конечно, результат getdate () будет отличаться при каждом выполнении и каждом вызове getdate (). Это повлияет на результат.

Кстати, наличие * в вашем списке SELECT не очень хорошая идея. Вы должны только вернуть необходимые столбцы. Это делает результаты просмотра уязвимыми для изменений в базовых таблицах.

Есть несколько других вещей, которые не прошли бы проверку кода, где я работаю, но это вроде ОТ, я думаю.

...