sql - 列别名引用

标签 sql oracle ora-00904

此查询将数字分解为其受人尊敬的位置,例如千位、百位、五十位等。问题是我无法通过其别名来引用该列。在 Oracle 中,我收到此错误:

ora-00904: "twos": invalid identifier



但代码在 MS Access 中运行良好

询问:
SELECT 
    BT, 
    CNO, 
    AMT,  
    TRUNC(AMT/1000) AS THS, 
    TRUNC((AMT-(THS*1000))/500) AS FIVHUN, 
    TRUNC((AMT-((THS*1000)+(FIVHUN*500)))/100) AS HUND, 
    TRUNC((AMT-(((THS*1000)+(FIVHUN*500))+(HUND*100)))/50) AS FIF, 
    TRUNC((AMT-(((THS*1000)+(FIVHUN*500))+(HUND*100)+(FIF*50)))/20) AS TWENTY, 
    TRUNC((AMT-(((THS*1000)+(FIVHUN*500))+(HUND*100)+(FIF*50)+(TWENTY*20)))/10) AS TENS, 
    TRUNC((AMT-(((THS*1000)+(FIVHUN*500))+(HUND*100)+(FIF*50)+(TWENTY*20)+(TENS*10)))/5) AS FIVES, 
    TRUNC((AMT-(((THS*1000)+(FIVHUN*500))+(HUND*100)+(FIF*50)+(TWENTY*20)+(TENS*10)+(FIVES*5)))/2) AS TWOS, 
    TRUNC((AMT-(((THS*1000)+(FIVHUN*500))+(HUND*100)+(FIF*50)+(TWENTY*20)+(TENS*10)+(FIVES*5)+(TWOS*2)))/1) AS ONES 
FROM 
    EMPLOYER;

最佳答案

您只能在外部选择中引用列别名,因此除非您重新计算每列的所有先前值,否则您需要嵌套每个级别,这有点难看:

select bt, cno, amt, ths, fivhun, hund, fif, twenty, tens, fives, twos,
    trunc((amt-(ths*1000)-(fivhun*500)-(hund*100)-(fif*50)-(twenty*20)
        -(tens*10)-(fives*5)-(twos*2))/1) as ones
from (
    select bt, cno, amt, ths, fivhun, hund, fif, twenty, tens, fives,
        trunc((amt-(ths*1000)-(fivhun*500)-(hund*100)-(fif*50)-(twenty*20)
            -(tens*10)-(fives*5))/2) as twos
    from (
        select bt, cno, amt, ths, fivhun, hund, fif, twenty, tens,
            trunc((amt-(ths*1000)-(fivhun*500)-(hund*100)-(fif*50)-(twenty*20)
                -(tens*10))/5) as fives
        from (
            select bt, cno, amt, ths, fivhun, hund, fif, twenty,
                trunc((amt-(ths*1000)-(fivhun*500)-(hund*100)-(fif*50)
                    -(twenty*20))/10) as tens
            from (
                select bt, cno, amt, ths, fivhun, hund, fif,
                    trunc((amt-(ths*1000)-(fivhun*500)-(hund*100)
                        -(fif*50))/20) as twenty
                from (
                    select bt, cno, amt, ths, fivhun, hund,
                        trunc((amt-(ths*1000)-(fivhun*500)
                            -(hund*100))/50) as fif
                    from (
                        select bt, cno, amt, ths, fivhun,
                            trunc((amt-(ths*1000)-(fivhun*500))/100) as hund
                        from (
                            select bt, cno, amt, ths,
                                trunc((amt-trunc(ths*1000))/500) as fivhun
                            from (
                                select bt, cno, amt,
                                    trunc(amt/1000) as ths from employer

                            )
                        )
                    )
                )
            )
        )
    )
);

...这给出了类似的东西:
 BT CNO              AMT     THS FIVHUN HUND FIF TWENTY TENS FIVES TWOS ONES
--- --- ---------------- ------- ------ ---- --- ------ ---- ----- ---- ----
  1   2      123,456,789  123456      1    2   1      1    1     1    2    0
  3   4       87,654,321   87654      0    3   0      1    0     0    0    1
  5   6        1,234,567    1234      1    0   1      0    1     1    1    0

不是更漂亮,而是递归版本,主要是为了我自己的娱乐:
with t as (
    select bt, cno, amt, x,
        case x when 1 then 1000 when 2 then 500 when 3 then 100
            when 4 then 50 when 5 then 20 when 6 then 10 when 7 then 5
            when 8 then 2 when 9 then 1 end as bill
    from employer
    cross join (select level as x from dual connect by level < 10)
),
r (bt, cno, amt, x, y, running) as (
    select t.bt, t.cno, t.amt, 0 as x, 0 as y, 0 as running
    from t
    where t.x = 1 -- could be any x, just want one row per bt/cno
    union all
    select t.bt, t.cno, t.amt, t.x,
        trunc((t.amt - r.running)/t.bill) as y,
        r.running + (t.bill * trunc((t.amt - r.running)/t.bill)) as running
    from t 
    join r on r.bt = t.bt and r.cno = t.cno and r.x = t.x - 1
)   
select bt, cno, amt,
    max(case when x = 1 then y else 0 end) as ths,
    max(case when x = 2 then y else 0 end) as fivhun,
    max(case when x = 3 then y else 0 end) as hund,
    max(case when x = 4 then y else 0 end) as fif,
    max(case when x = 5 then y else 0 end) as twenty,
    max(case when x = 6 then y else 0 end) as tens, 
    max(case when x = 7 then y else 0 end) as fives, 
    max(case when x = 8 then y else 0 end) as twos,  
    max(case when x = 9 then y else 0 end) as ones
from r
group by bt, cno, amt
order by bt, cno;
t 公用表表达式 (CTE) 只是将真实数据与生成数字 1-9 的虚拟表进行交叉连接,并将纸币面额值(假设 Robert Co 是正确的)分配给每个级别以供以后使用。
r CTE 是递归的,我认为它只适用于 11gR2。联合的第一部分建立了到目前为止账单加起来的“总和”,这是零,因为这是递归的第一步。除了用于递归连接的 x 的虚拟零值之外,其余的列都没有使用。联合的第二部分从该级别的 amt 中减去上一级别的运行总数,找到该面额的完整钞票数量 - 这是我们实际想要报告的 - 并重新计算运行总数以包含该数字。每次循环时,账单的大小都会减少,而运行总额会增加。

所以这最终有很多行,每张账单的数量都是不同的行;实际上需要旋转以显示适当列下的值。这就是末尾的 max()group by 位所做的。

对于我的虚拟数据,它给出了相同的结果:
 BT CNO              AMT     THS FIVHUN HUND FIF TWENTY TENS FIVES TWOS ONES
--- --- ---------------- ------- ------ ---- --- ------ ---- ----- ---- ----
  1   2      123,456,789  123456      1    2   1      1    1     1    2    0
  3   4       87,654,321   87654      0    3   0      1    0     0    0    1
  5   6        1,234,567    1234      1    0   1      0    1     1    1    0

顺便说一句,我最初尝试使用 mod() (如 AndriyM 建议的)来简化它,但您不能独立计算每个值:
select bt, cno, amt,
    floor(           amt/1000) as ths,
    floor(mod(amt, 1000)/ 500) as fivhun,
    floor(mod(amt,  500)/ 100) as hund,
    floor(mod(amt,  100)/  50) as fif,
    floor(mod(amt,   50)/  20) as twenty,
    floor(mod(amt,   20)/  10) as tens,
    floor(mod(amt,   10)/   5) as fives,
    floor(mod(amt,    5)/   2) as twos,
    floor(mod(amt,    2)/   1) as ones
from employer
order by bt, cno;

 BT CNO              AMT     THS FIVHUN HUND FIF TWENTY TENS FIVES TWOS ONES
--- --- ---------------- ------- ------ ---- --- ------ ---- ----- ---- ----
  1   2      123,456,789  123456      1    2   1      1    0     1    2    1
  3   4       87,654,321   87654      0    3   0      1    0     0    0    1
  5   6        1,234,567    1234      1    0   1      0    0     1    1    1

大多数值是相同的,但 tens 都是 0ones 都是 1 。后者很容易解释,尽管更多的是为什么它们不应该都是 1 的问题。如果 fives 的值是 1 ,则要拆分的剩余金额将变为偶数,因此 ones 必须是 0 。类似地,tens 值没有考虑 fif。因此,这种简单查询无法处理的值之间存在依赖关系。您可以调整问题列以将其考虑在内,当然,冒着引入细微错误的风险。

关于sql - 列别名引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12988637/

相关文章:

sql - 如何在 WHERE 子句中使用临时列名?

SQL:在特定列中查找与另一个相同表相似的行

C# 以编程方式执行 TNSPing

php - 在 PHP 代码中隐藏数据库登录信息

sql - Oracle SQL 根据列值将连续数字分配给一个子集

sql - 在 where 子句中使用数字表作为过滤器

oracle - plsql oracle检查约束错误

sql - Oracle显示所有工资高于部门平均水平的员工

php - 数据库设计选择

mysql - 如何连接具有多个条件的多个表