【问题标题】:Oracle SQL weird issue with my below query我的以下查询的 Oracle SQL 奇怪问题
【发布时间】:2013-12-11 06:32:45
【问题描述】:

我有一个名为 RULE_READOUT_VIEW 的视图,其中包含名为 'READOUTTYPE' 和 'CUSTOMERNUMBER' 的 varchar 列。

当我运行以下计数查询时,我只得到一行作为输出。

SELECT count(1) FROM RULE_READOUT_VIEW
WHERE READOUTTYPE = '5' AND CUSTOMERNUMBER = 'NSMPF';

但是,如果运行这个其他查询,我会得到不同的结果,这次计数是 11 行,这是预期的结果。

SELECT count(1) FROM RULE_READOUT_VIEW
WHERE to_char(READOUTTYPE) = '5' AND CUSTOMERNUMBER = 'NSMPF';

我不确定为什么第一个查询不起作用,只有当我在列上强制一个 to_char 时,我才能得到正确的答案。我寻找答案,但找不到任何令人满意的东西。有人可以解释一下为什么会出现这种行为吗?

我也运行了以下查询来检查每一行的长度

SELECT READOUTTYPE, TO_CHAR(READOUTTYPE), LENGTH(READOUTTYPE), LENGTHB(READOUTTYPE) FROM teoss.RULE_READOUT_VIEW WHERE to_char(READOUTTYPE) = '5' and CUSTOMERNUMBER = 'NSMPF';

我有 11 行作为输出,所有列的长度都是 1。

5 5 1 1
5 5 1 1
5 5 1 1
5 5 1 1
5 5 1 1
5 5 1 1
5 5 1 1
5 5 1 1
5 5 1 1
5 5 1 1
5 5 1 1

所以列也没有任何额外的空格。

查看 DDL:

SELECT --ro.creation_date,
ro.POINT_OF_TIME_DAY                      AS pointOfReadoutDay,
ro.POINT_OF_TIME_HOUR                     AS pointOfReadoutHour,
ro.POINT_OF_TIME_MINUTE                   AS pointOfReadoutMinute,
rot.NAME                                  AS readoutType,
cp.IDENTIFICATION                         AS customerNumber,
mp.METER_POINT_NUMBER                     AS meterPointNumber,
ro.ORDER_REFERENCE                        AS readoutOrderNumber,
mtrefstat.NAME                            AS meterStatus,
mt.ADDRESS                                AS address,
mt.SERIAL_NUMBER                          AS serialNumber,
mt.UPPER_SERIAL_NUMBER                    AS upperSerialNumber,
servrefstat.NAME                          AS serverStatus,
mus.MAC                                   AS hostname,
mserv.IP_ADDRESS                          AS ipAddress ,
mt.device_id                              AS deviceID,
GET_ACCESSCONNECTION_TYPE(mus.device_id)  AS ACCESSCONNECTIONTYPE,
FUNC_LIST_OBIS_NAME(ro.order_read_out_id) AS refObisList,
mt.METER_NUMBER                           AS meterNumber
FROM ORDER_READ_OUT ro,
REF_METER_READ_OUT_TYPE rot,
COMPANY cp,
METER_POINT mp,
METER mt,
MAP_DEVICE_STATUS mtstat,
REF_STATUS mtrefstat,
DEVICE servdev,
MUS mus,
METERING_SERVER mserv,
MAP_DEVICE_STATUS servstat,
REF_STATUS servrefstat,
MAP_ORD_RD_OUT_CNTRCT_MT_PT mocm,
MAP_CONTRACT_METER_POINT mcm,
CONTRACT co
WHERE -- order readout relation has to be valid
mocm .ORDER_READ_OUT_ID         = ro.ORDER_READ_OUT_ID
AND ro.REF_METER_READ_OUT_TYPE_ID = rot.REF_METER_READ_OUT_TYPE_ID
AND ro.ACTIVATION_DATE           <= SYSTIMESTAMP
AND (ro.DEACTIVATION_DATE        IS NULL
OR ro.DEACTIVATION_DATE           > SYSTIMESTAMP)
-- contract relation has to be valid ...
AND mocm.MAP_CONTRACT_METER_POINT_ID = mcm.MAP_CONTRACT_METER_POINT_ID
AND mcm.CONTRACT_ID                  = co.CONTRACT_ID
AND co.COMPANY_ID                    = cp.COMPANY_ID
AND ((co.BEGIN                      IS NULL
AND mcm.BEGIN                       <= SYSTIMESTAMP)
OR (co.BEGIN                        <= SYSTIMESTAMP
AND mcm.BEGIN                       <= SYSTIMESTAMP
AND mcm.begin                       >= co.begin))
AND (mcm.end                        IS NULL
OR mcm.end                          >= SYSTIMESTAMP)
AND ((co.END                        IS NULL)
OR (co.END                          >= SYSTIMESTAMP
AND ((mcm.end                       IS NULL)
OR (mcm.end                         <= co.end))))
-- meter point relation has to be valid ...
AND mcm.METER_POINT_ID   = mp.METER_POINT_ID
AND mp.METER_POINT_ID    = mt.METER_POINT_ID
AND mt.DEVICE_ID         = mtstat.DEVICE_ID
AND mtstat.REF_STATUS_ID = mtrefstat.REF_STATUS_ID
AND (mtrefstat.name      = 'active'
OR mtrefstat.name        = 'toBeRemoved')
AND mtstat.STATUS_DATE   =
(SELECT MAX(mds.status_date)
FROM Map_device_status mds
WHERE mds.device_id = mtstat.DEVICE_ID
)
-- mus/metering-server relation has to be valid
AND servdev.DEVICE_ID =
(SELECT FUNC_FIND_SERVER_DEV_ID(mt.DEVICE_ID) FROM DUAL
)
AND servdev.DEVICE_ID      = mus.DEVICE_ID(+)
AND servdev.DEVICE_ID      = mserv.DEVICE_ID(+)
AND servdev.DEVICE_ID      = servstat.DEVICE_ID
AND servstat.REF_STATUS_ID = servrefstat.REF_STATUS_ID
AND (servrefstat.name      = 'active'
OR servrefstat.name        = 'toBeRemoved')
AND servstat.STATUS_DATE   =
(SELECT MAX(mds.status_date)
FROM Map_device_status mds
WHERE mds.device_id = servstat.DEVICE_ID
)
AND ro.CREATION_DATE =
(SELECT MAX(t9.CREATION_DATE)
FROM ORDER_READ_OUT t9
WHERE t9.point_of_time_day        = ro.point_of_time_day
AND t9.point_of_time_hour         = ro.point_of_time_hour
AND t9.point_of_time_minute       = ro.point_of_time_minute
AND t9.ref_meter_read_out_type_id = ro.ref_meter_read_out_type_id
AND t9.ORDER_READ_OUT_ID         IN
(SELECT t5.ORDER_READ_OUT_ID
FROM map_ord_rd_out_cntrct_mt_pt t5
WHERE t5.MAP_CONTRACT_METER_POINT_ID = mcm.MAP_CONTRACT_METER_POINT_ID
));

计划sn-p: explain plan for SELECT count(1) FROM RULE_READOUT_VIEW WHERE READOUTTYPE = '5' AND CUSTOMERNUMBER = 'NSMPF';

| 19 |按索引 ROWID 访问表| REF_METER_READ_OUT_TYPE | 1 | 7 | 1 |
| 20 |索引唯一扫描 | UQ_REF_METER_READ_OUT_TY_NAME | 1 | | 0

    explain plan for SELECT count(1) FROM RULE_READOUT_VIEW WHERE to_char(READOUTTYPE) = '5' AND CUSTOMERNUMBER = 'NSMPF';

| 14 |索引快速全扫描 | PK_METERREADTYPE | 1 | 7 | 1 |
| 15 |索引快速全扫描 | UQ_REF_METER_READ_OUT_TY_NAME | 1 | 7 | 1 |

【问题讨论】:

  • 确保您的 readouttype 列是干净的,即其中没有包含空格的条目。
  • 我检查了列值。我在之前或之后看不到任何额外的空格。此外,如果我在查询下运行,我也会得到 11 行 SELECT count(1) FROM RULE_READOUT_VIEW WHERE READOUTTYPE like '5' AND CUSTOMERNUMBER = 'NSMPF'。问题似乎只有当我单独使用相等运算符时。
  • 你是怎么检查的?您的两个“工作”查询都表明您的读数列不仅包含“5”,而且还有一些其他类似空格的内容。
  • 为了检查没有空格,我运行了以下查询: SELECT count(1) FROM RULE_READOUT_VIEW WHERE READOUTTYPE = 5 AND CUSTOMERNUMBER = 'NSMPF' where oracle 应该尝试对列 readoutype 进行隐式 to_number 转换.该查询也返回了 11 行。如果有空格,oracle 应该在隐式类型转换期间抛出错误。我说的对吗?
  • 没有。您的 to_char 做同样的事情,隐式转换将“剥离”空白。将计数替换为length(readouttype)utl_raw.cast_to_raw(readouttype)

标签: sql oracle


【解决方案1】:

我怀疑您可能在 REF_METER_READ_OUT_TYPE.NAME 上有损坏的索引,查询结果之间的差异是由是否使用索引的差异来解释的。

尝试删除并重新创建索引(不要重建,因为这可能会保留任何损坏)。

【讨论】:

  • 我删除并重新创建了索引。还是同样的问题。
  • 你能发布两个查询的执行计划吗?
  • 由于我无法发布完整的计划,因为它超出了允许的大小,我已经使用 readoutype 列的计划的 sn-p 更新了我的查询。
【解决方案2】:

想法 1:

当你运行这个查询时,你应该找回有问题的行:-

SELECT READOUTTYPE, TO_CHAR(READOUTTYPE), LENGTH(READOUTTYPE) 
FROM RULE_READOUT_VIEW
WHERE to_char( READOUTTPYE ) = '5'
AND READOUTTYPE != '5'
AND CUSTOMERNUMBER = 'NSMPF';

也许用这个查询的结果更新你的问题,看看它是否让任何人都清楚。

想法 2:

试试这个:

CREATE TABLE TEMP_RULE_READOUT AS 
SELECT * FROM RULE_READOUT_VIEW
WHERE CUSTOMERNUMBER = 'NSMPF';

SELECT count(1) FROM TEMP_RULE_READOUT
WHERE READOUTTYPE = '5' AND CUSTOMERNUMBER = 'NSMPF';

SELECT count(1) FROM RULE_READOUT_VIEW
WHERE to_char(READOUTTYPE) = '5' AND CUSTOMERNUMBER = 'NSMPF';

当数据被具体化到表中时,看看这是否正确工作会很有趣。

【讨论】:

  • 我运行了您提出的查询。以下查询没有返回任何行 SQL> SELECT READOUTTYPE, TO_CHAR(READOUTTYPE), LENGTH(READOUTTYPE), LENGTH(TO_CHAR(READOUTTYPE)) FROM teoss.RULE_READOUT_VIEW WHERE TO_CHAR(READOUTTYPE ) = '5' AND READOUTTYPE != '5 ' 和客户编号 = 'NSMPF';未选择任何行
  • 您的 Idea2 似乎产生了正确的结果。如果我在创建的临时表上运行查询,那么我会得到正确的结果。但是我仍然不明白它是如何在从视图创建但不适用于视图本身的表上工作的?
  • @thiyaga 我认为数据本身没问题,您遇到了某种描述的 Oracle 错误,这是由不同查询导致的计划差异触发/隐藏的。
猜你喜欢
  • 1970-01-01
  • 2011-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多