oracle 查询记录上方记录的SQL语句

wkyowqbh  于 2023-05-16  发布在  Oracle
关注(0)|答案(5)|浏览(124)

我有一个数据库,里面有600万条记录,这些记录是从PDF会计文档中提取的。
在数据库中,我在列A中有一条记录,其值为:“员工姓名”。以及B列中从1到6百万的递增索引。
值“EmployeeName”上方的记录有时是雇员的姓名,带有一个数字。
有时它是一个空值,有时它是一个数字。在这两种情况下,员工姓名都将列在该记录的上方。
示例:
| 一个|B|
| --------------|--------------|
| “约翰·史密斯12345”|十九岁|
| “员工姓名”|二十|
| 《Something Else》|二十一|
| 《Something Else》|二十二|
| “简·史密斯56789”|二十三|
| “空”|二十四|
| “员工姓名”|二十五|
| 《Something Else》|二十六|
| 《Something Else》|二十七个|
| “小雅各布·约翰·史密斯”|二十八|
| “九八七六五”|二十九|
| “员工姓名”|三十|
| 《Something Else》|三十一|
我需要雇员的名字。过滤掉这个数字可能是一个不错的奖励,但现在我很高兴至少能得到这个名字。

SELECT DISTINCT A 
FROM PAYROLL_RECORDS
WHERE B IN (SELECT (B - 1) FROM PAYROLL_RECORDS 
            WHERE A = 'Employee Name') 
ORDER BY A;

如果名称总是位于其顶部的行,则这将非常有效。
我可能需要一个SELECT语句,它可以抓取上面的两行,并检查A列中是否有CHARACTERS(而不是数字或NULL)。
我需要一份所有员工姓名的清单,完整扫描所有600万条记录。
想法?

daupos2t

daupos2t1#

选择这两行并使用regex丢弃错误的行

SELECT DISTINCT A FROM PAYROLL_RECORDS
WHERE
   (B IN (SELECT (B - 1) FROM PAYROLL_RECORDS WHERE A = 'Employee Name') OR
    B IN (SELECT (B - 2) FROM PAYROLL_RECORDS WHERE A = 'Employee Name')) AND
   NOT regexp_like(A, '^(\d+|null)$') 
ORDER BY A;

成功也取决于“其他东西”中的内容。您可能还必须丢弃正则表达式中的其他值。

a0zr77ik

a0zr77ik2#

我知道您希望列中每次出现字符串'Employee Name'时的“前一个”非空值和非数字值。
我推荐使用带有条件表达式和ignore nulls选项的lag

select distinct employee_name
from (
    select p.*,
        lag(case when not regexp_like(a, '^\d+$') then a end)
            ignore nulls 
            over(order by b) employee_name
    from payroll_records p
) p
where a = 'Employee Name'

技巧是让lag()中的表达式将所有数字值转换为null s,以便ignore null s选项在查找实际雇员姓名时跳过它们。
请注意:

  • 这将执行单表扫描
  • 无论需要跳过多少行,此操作都有效
    Demo-在nbk的小提琴(谢谢nbk)。
drkbr07n

drkbr07n3#

您可以使用窗口函数LEAD检查两行的值

WITH CTE AS
(SELECT
  "A",
  LEAD("A") OVER(ORDER BY "B") row1
  ,
  LEAD("A",2) OVER(ORDER BY "B") row2
  FROM 
PAYROLL_RECORDS)
SELECT "A" FROM CTE 
  WHERE (row1 = 'Employee Name' OR row2 = 'Employee Name') 
  AND (NOT REGEXP_LIKE("A", '^[[:digit:]]+$'))
一个
约翰·史密斯12345”
简·史密斯56789
小雅各布·约翰·史密斯

fiddle
如果你有空格,你可以TRIM A

WITH CTE AS
(SELECT
  "A",
  LEAD(TRIM("A")) OVER(ORDER BY "B") row1
  ,
  LEAD(TRIM("A"),2) OVER(ORDER BY "B") row2
  FROM 
PAYROLL_RECORDS)
SELECT "A" FROM CTE 
  WHERE (row1 LIKE  'Employee Name' OR row2 LIKE 'Employee Name') 
  AND (NOT REGEXP_LIKE("A", '^[[:digit:]]+$'))
一个
约翰·史密斯12345”
简·史密斯56789
小雅各布·约翰·史密斯

fiddle

3z6pesqy

3z6pesqy4#

不需要联接或子查询。在where子句的帮助下,您可以从A列中删除null和数字值,然后lag()over()窗口函数将帮助您获得每个“员工姓名”的前一个值。第一行始终为null
查询:

select distinct case when A='Employee Name' then lag(regexp_replace(A, '[0-9]', ''))over(order by B) end as A from PAYROLL_RECORDS 
  where A is not null and VALIDATE_CONVERSION(A AS NUMBER) <> 1

输出:
| 一个|
| --------------|
| 联系我们|
| 约翰·史密斯|
| 简·史密斯|
| 小雅各布·约翰·史密斯|
fiddle
如果你想删除第一行空值,那么:
查询:

select A from
(select distinct case when A='Employee Name' then lag(regexp_replace(A, '[0-9]', ''))over(order by B) end as A from PAYROLL_RECORDS 
  where A is not null and VALIDATE_CONVERSION(A AS NUMBER) <> 1 )
 t where A is not null

输出:
| 一个|
| --------------|
| 约翰·史密斯|
| 简·史密斯|
| 小雅各布·约翰·史密斯|
fiddle
在Oracle 10 g中,validate_conversion()不可用。您可以使用REGEXP_LIKE()
查询:

select A from
(select distinct case when A='Employee Name' then lag(regexp_replace(A, '[0-9]', ''))over(order by B) end as A from PAYROLL_RECORDS 
  where A is not null and not REGEXP_LIKE(A, '^[[:digit:]]+$'))
 t where A is not null

输出:
| 一个|
| --------------|
| 约翰·史密斯|
| 简·史密斯|
| 小雅各布·约翰·史密斯|
fiddle

uurv41yg

uurv41yg5#

使用第一个CTE从值中删除任何空值和数字,然后将“员工姓名”标记为1,将其他值标记为0。
第二个CTE用于检索每个“员工姓名”行的前面的行。

WITH CTE AS (
   SELECT A, B, CASE WHEN A = 'Employee Name' then 1 else 0 end as flaged
   FROM PAYROLL_RECORDS
   WHERE A is not null AND (NOT REGEXP_LIKE("A", '^[[:digit:]]+$'))
),
CTE2 AS (
  SELECT A, B, flaged, lead(flaged) over (order by B) as NameRank
  FROM CTE
)
select A
from CTE2
where NameRank = 1

Demo here

相关问题