View的Count(*)在SQL Server上返回不同的结果

时间:2019-05-07 16:43:25

标签: sql-server tsql sql-server-2012

我正在处理ETL优化问题,这需要创建一个可以与最终表合并的临时表。目前,我有几个用于加载最终表的视图,这需要很多时间。我试图从视图中获取SQL逻辑并创建了一个临时表,并注意到临时表中的值与最终表中的值不匹配。为了更深入地了解,我在视图上几次运行count(*),并注意到每次运行的总行数结果相差约10/15行。该视图有9个表中的16列,每天仅加载一次。因此,运行count(*)的基础数据的时间不会改变,但是视图中计数的结果确实会改变。

这是在SQL Server 2016服务器上。我尝试查看View逻辑,没有什么比这更奇怪了。我尝试在加载此视图的表上执行count(*),并且表的计数不变。我还尝试从视图逻辑中创建2列表以简化问题,并尝试了EXCEPT命令,并且在从相同的精确视图逻辑创建的2列表之间仍然产生约20行不一致的值。

这是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 :(得分:0)

您的查询在底部附近有between cast(dateadd(month,-12,getdate()) as date) and cast(getdate() as date)。当然,每次执行和每次调用getdate()时,getdate()的结果都会有所不同。那会影响结果。

顺便说一句,在您的SELECT列表中加*不是一个好主意。您应该只返回所需的列。这样会使视图结果容易受到基础表中的更改的影响。

在我工作的地方,还有其他一些事情无法通过代码审查,但是我认为这有点过时。

答案 1 :(得分:0)

这个评论太长了。在视图中使用*是一个非常糟糕的主意。更改基表时,不仅视图不会更新(除非执行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

请注意,在最后一次从视图中选择如何在Col2中显示来自Col3的数据。如果这不能说服您停止在视图中(几乎所有地方)都使用*,我不知道会怎么做。