none
PHP访问SQL Server 2005 的一个神奇错误,查询出的资料不对 RRS feed

  • 问题

  • 环境:linux 下 通过 freeTDS 访问 sql server 2005

    异常现象为不确定什么时候会 查询结果错误.

    最初我是通过 select * from tb 查询,发现漏了几条记录,后来发现用  select count(*) from tb 发现通过 php去查询就是会少掉这几笔.

    于是我加了条件缩小查询范围,结果发现少掉的记录是固定的,这些记录通过php去查询时就是隐形的.

    不死心的我在命令行下直接使用 tsql 查询,结果却又是正常的.

    接下来,我通过 SQL Server Profiler 去trace,发现sql server 接收到sql 语句是没有问题的,同样的语句,得到不同的结果.

    因为我担心是不是数据在传输过程中产生的错误,我直接将查询结果写到另一个表里,按道理来说这样应该能得到相同的结果.

    但最神奇的事情发生了,就算数据不出sql server ,通过php送来的sql 查询就是看不到那些记录.

    insert into temptb(rows,writetime) select count(*) ,getdate() from ym_employees_wechat_all where  empno like 'y1503%'

    说明:

    1. 查询的这个表中的数据是不变的

    2. 不是每个表都会有这种问题

    3.同一个表中的数据复制到其它数据库中可以正常查询

    那么,那位大神来帮忙分析分析,问题到底出在哪?


    2017年9月6日 8:09

答案

  • 正好没事,就仔细看了你发的trc文件。问题找到了,默认的配置项不一样,导致查询结果差异。测试SQL如下:

    set concat_null_yields_null on
    SELECT count(*)  FROM [dbo].[tb001_view]
    
    set concat_null_yields_null off
    SELECT count(*)  FROM [dbo].[tb001_view]
    
    --返回结果
    -----------
    13
    (1 行受影响)
    
    -----------
    10
    (1 行受影响)
    
    


    family as water

    • 已标记为答案 老施 2017年9月28日 5:49
    2017年9月26日 7:17

全部回复

  • 补充一下:

    其实我是查询的view,不是直接查询table,但是这个view没有index,所以可以看作是查询table.

    神奇继续,直接查询table,结果会是正确的.

    2017年9月6日 9:33
  • 查 view 改成子查询呢?

    也就是 select * from view

    改成

    select * from ( view 里面的查询 ) as view

    2017年9月7日 1:19
  •  还是一样的,你可以看到我最前面为了避免数据传输过程中出问题,直接将数据留在sql server 内部(将查询结果insert到另一个表)都出这问题
    2017年9月7日 7:30
  • 如果用子查询的方式也有问题,那似乎问题跟 VIEW 无关

    而直接访问表又没有问题,那也跟环境和处理的方式无关

    这似乎就是死局了

    如果有条件,试试把有问题的表在另一台机器上建,然后把数据导过去测试一下

    2017年9月7日 9:10
  • 补充一下:

    其实我是查询的view,不是直接查询table,但是这个view没有index,所以可以看作是查询table.

    神奇继续,直接查询table,结果会是正确的.


    查询view跟查询table是否结果一样,不是看有没有index,而是看有没有join,有没有where

    想不想时已是想,不如不想都不想。

    2017年9月7日 9:15
    版主
  • 你的PHP是什么版本?如果太新會有支援上的問題。
    2017年9月7日 10:18
  • 所以才觉得这是一个神奇的问题.

    现在可以确定的是和这个view的写法有关,重写了个写法有点区别的view,得到的结果就是正确的.

    在我的认知中,sql server提供访问端口,client 建立连接后提交sql,然后SQL server处理完后返回结果给client.

    现在看来并不是,不然就不会出现这种怪事了.

    另:

    我把这个表和视图复制到了另一台机器上,问题有了新变化.

    查询view时,连用tsql 命令行执行也会得到错误结果,直接查询table还是一样都正确的.

    2017年9月8日 7:37
  • php 5.4.16+freetds 0.95.81

    php 5.2.17+freetds 0.64.1

    这两个平台上执行结果相同,其它的我没试

    2017年9月8日 7:40
  • 你的 VIEW 用的什么神奇的写法?
    2017年9月8日 9:56
  • 上周杂事较多,先找到替代方法后就没再管了,今天抽空把资料简洁一下,再放上来.

    一个table,先写了个view,然后再写个view查前面的view,用于条件判断.

    因为原来写view的早就不在了,所以也不知道为什么要这样写.

    tb001:

    empno,COMPANY_CODE,EMAIL,MOBILE_PHONE
    Y15036,02,Y15036@domain.com,
    Y05043,02,Y05043@domain.com,
    Y11055,02,Y11055@domain.com,
    Y11104,02,Y11104@domain.com,99Y11104
    Y02131,02,Y02131@domain.com,99Y02131
    Y04001,02,Y04001@domain.com,99Y04001
    Y04061,02,Y04061@domain.com,99Y04061
    Y05039,02,Y05039@domain.com,99Y05039
    Y05110,02,Y05110@domain.com,99Y05110
    Y13029,02,Y13029@domain.com,99Y13029
    Y13008,02,Y13008@domain.com,99Y13008
    Y13043,02,Y13043@domain.com,99Y13043
    Y13052,02,Y13052@domain.com,99Y13052

    view1:

    ALTER VIEW [dbo].[tb001_view_New]
    AS
    SELECT     EMPNO,  MOBILE_PHONE_HR, EMAIL, COMPANY_CODE, 
     CASE WHEN company_code = 02 THEN '+886' + substring(replace(MOBILE_PHONE_HR, '-', ''), 2, 9) ELSE MOBILE_PHONE_HR END AS MOBILE_PHONE
    
    FROM         (SELECT     EMPNO, [COMPANY_CODE], CASE WHEN MOBILE_PHONE = '' THEN NULL 
                             ELSE MOBILE_PHONE END AS MOBILE_PHONE_HR, 
    						 CASE WHEN EMAIL = 'admin@domain.com' THEN NULL ELSE EMAIL END AS EMAIL
    
                           FROM          dbo.tb001
                           ) AS t
    WHERE     (MOBILE_PHONE_HR IS NOT NULL) OR
                          (EMAIL IS NOT NULL)

    view2:

    ALTER VIEW [dbo].[tb001_view]
    AS
    SELECT     EMPNO,  MOBILE_PHONE
    FROM         dbo.tb001_view_New
    WHERE     (EMPNO NOT IN
                              (SELECT     EMPNO
                                FROM          tb001_view_new AS tb001_view_new_4
                                WHERE      (MOBILE_PHONE IN
                                                           (SELECT     MOBILE_PHONE
                                                             FROM          dbo.tb001_view_new AS tb001_view_new_3
                                                             WHERE      (MOBILE_PHONE IS NOT NULL)
                                                             GROUP BY MOBILE_PHONE
                                                             HAVING      (COUNT(1) > 1)))
                                UNION
                                SELECT     EMPNO
                                FROM         dbo.tb001_view_new AS tb001_view_new_2
                                WHERE     (EMAIL IN
                                                          (SELECT     EMAIL
                                                            FROM          dbo.tb001_view_new AS tb001_view_new_1
                                                            WHERE      (EMAIL IS NOT NULL)
                                                            GROUP BY EMAIL
                                                            HAVING      (COUNT(1) > 1)))))

    除了PHP中, SELECT count(*)  FROM [bonne].[dbo].[tb001_view] 会得到全部13笔

    在PHP中, SELECT count(*)  FROM [bonne].[dbo].[tb001_view] 只会得到MOBILE_PHONE不为空的10笔.

    <?PHP
    $con=mssql_connect("c10133013","sa","Ab-123456");
    $db=mssql_select_db("bonne",$con);
    
    $query = " select count(*) from tb001_view   ";
    $results= mssql_query($query);
    while ($row = mssql_fetch_array($results)){
        echo '->'.$row[0].PHP_EOL;
    }
    
    [root@ksyoweb01 tmp]# php tt.php
    ->10
    

    大神来帮忙解惑了~~


    2017年9月19日 2:35
  • 有空帮忙解下惑呢,大神
    2017年9月22日 3:21
  • 有空帮忙解下惑呢,大神
    2017年9月22日 3:30
  • 查询view时,连用tsql 命令行执行也会得到错误结果,直接查询table还是一样都正确的.

    Hi 老施,

    事实上,确实是sql server提供访问端口,client提交的query都是在sql server中执行的,然后返回结果给client。

    而且我们拿了几台机子测试了你的query,都没法重现这个问题,你可以尝试升级一下SQL Server和驱动。

    Best Regards,

    Teige


    MSDN Community Support<br/> Please remember to click &quot;Mark as Answer&quot; the responses that resolved your issue, and to click &quot;Unmark as Answer&quot; if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact <a href="mailto:MSDNFSF@microsoft.com">MSDNFSF@microsoft.com</a>.



    2017年9月22日 7:19
    版主
  • Hello,Teige!

    我不知道你的运行环境是不是在Linux下,所以没法重现这个问题.

    我这边使用了两台linux安装有不同版本的php freetds 连接 不同版本的 sql server 都会有同样问题.

    SQL SERVER试过两个2015 Ent和2014 Express

    ClientA: CentOS5+PHP 5.2.17+FreeTDS 0.64

    ClientB:CentOS7+PHP 5.4.16+FreeTDS 0.95.81

    freetds.conf 中使用的tds version 为8.0

    为了确保不是偶然现象,我新建了db,并重新试了一下,结果还是一如之前所述.

    这里没法上传附件,所以我把测试的tabel和view打包了放在下面

    链接:pan.baidu.com/s/1o8jz7se 密码:0a7n

    最后,如果真的query只在内部执行,那就解释不了了..

    对应的截图和trace也那上面了.

    2017年9月26日 2:51
  • 试过在用sql profile抓取sql的时候开启真实的执行计划么,对比一下差别。


    family as water

    2017年9月26日 6:54
  • 不用FreeTDS ,换其他方式呢?ODBC,PDO等试试?

    可能这些lib在执行命令之前设置的执行环境是不一样的(set xxx之类),你可以仔细对比一下抓取的sql的上下文。


    family as water

    2017年9月26日 7:06
  • 正好没事,就仔细看了你发的trc文件。问题找到了,默认的配置项不一样,导致查询结果差异。测试SQL如下:

    set concat_null_yields_null on
    SELECT count(*)  FROM [dbo].[tb001_view]
    
    set concat_null_yields_null off
    SELECT count(*)  FROM [dbo].[tb001_view]
    
    --返回结果
    -----------
    13
    (1 行受影响)
    
    -----------
    10
    (1 行受影响)
    
    


    family as water

    • 已标记为答案 老施 2017年9月28日 5:49
    2017年9月26日 7:17
  • 不用FreeTDS ,换其他方式呢?ODBC,PDO等试试?

    可能这些lib在执行命令之前设置的执行环境是不一样的(set xxx之类),你可以仔细对比一下抓取的sql的上下文。


    family as water

    sql profile trace 结果表明,执行的sql 是一样的. set 也是一样,只有一句 set textsize 64512 


    2017年9月26日 7:18
  • 找的地方不对,在这个session开始的地方找。


    family as water

    2017年9月28日 1:53
  • 正好没事,就仔细看了你发的trc文件。问题找到了,默认的配置项不一样,导致查询结果差异。测试SQL如下:

    set concat_null_yields_null on
    SELECT count(*)  FROM [dbo].[tb001_view]
    
    set concat_null_yields_null off
    SELECT count(*)  FROM [dbo].[tb001_view]
    
    --返回结果
    -----------
    13
    (1 行受影响)
    
    -----------
    10
    (1 行受影响)
    


    family as water

    非常感谢, 这就是功力的体现.

    老司机才知道因为空的原因导致查询结果不正确那就要从跟这个有关的地方入手..

    2017年9月28日 5:49