我的sql查询到oracle查询的转换

Posted

技术标签:

【中文标题】我的sql查询到oracle查询的转换【英文标题】:My sql query to oracle query conversion 【发布时间】:2017-12-15 12:26:55 【问题描述】:

我有以下数据库结构

CREATE TABLE CUSTOMERS (
 custid char(4) constraint cust_pk primary key,
 firstname varchar(20),
 lastname varchar(25),
 city varchar(20),
 country varchar(20),
 creditlimit number(8,2)
);

CREATE TABLE PRODUCTS (
 prodID char(4) constraint prod_pk primary key,
 pname varchar(20),
 description varchar(50),
 category varchar(7), -- product category
 listprice numeric(5,2), -- list price
 weight numeric(4,1) -- weight 
);

CREATE TABLE SALES (
 saleno char(5) constraint sales_pk PRIMARY KEY,
 sdate date,
 paymentmethod varchar(20),
 custid char(4),
 constraint sales_fk_cust FOREIGN KEY (custid) references CUSTOMERS
);


CREATE TABLE SALESLINES (
 saleno char(5),
 prodid char(4),
 qty numeric(5),
 unitprice numeric(5,2),
 constraint sales_lines_pk PRIMARY KEY (saleno, prodid),
 constraint saleslines_fk_sale FOREIGN KEY (saleno) references SALES,
 constraint saleslines_fk_prod FOREIGN KEY (prodid) references PRODUCTS
);



INSERT INTO CUSTOMERS VALUES('C002', 'Ruby', 'Ringer','Springfield', 'Canada',125000);
INSERT INTO CUSTOMERS VALUES('C003', 'Bob', 'Bennett','Tucson','USA',50000);
INSERT INTO CUSTOMERS VALUES('C004', 'Pat', 'Rowling','Ottowa','Canada', 129000);
INSERT INTO CUSTOMERS VALUES('C905', 'Sue', 'Smith','Riverside','USA', 125000);
INSERT INTO CUSTOMERS VALUES('C005', 'Jim', 'Jason','New York', 'USA',25000);
INSERT INTO CUSTOMERS VALUES('C101', 'Darcy', 'Doe','Tucson','USA', 14500);
INSERT INTO CUSTOMERS VALUES('C104', 'Dan', 'Doe','Hermosillo','Mexico',10100);
INSERT INTO CUSTOMERS VALUES('C505', 'Sue', 'Smith','Tucson','USA', 19500);
INSERT INTO CUSTOMERS VALUES('C125', 'Bill', 'Jackson','Vancouver','Canada', 75000);




INSERT INTO PRODUCTS VALUES ('P051', '19" Monitor', 'Widescreen, black', 'Display', 114.95, 17.5);
INSERT INTO PRODUCTS VALUES ('P055', '27" Monitor', 'Widescreen, LCD ultra-sharp', 'Display', null, 50);
INSERT INTO PRODUCTS VALUES ('P012', 'Keyboard', 'Black, full size keys', 'Input', 14.75, 2);
INSERT INTO PRODUCTS VALUES ('P011', 'Keyboard', 'Ergonomic, soft touch keys', 'Input', 45.25, 2.5);
INSERT INTO PRODUCTS VALUES ('P074', 'Optical Mouse', '2-button mouse, basic', 'Input', 9.99, 1);
INSERT INTO PRODUCTS VALUES ('P075', 'Optical Mouse', 'Compact notebook optical mouse', 'Input', 24.99, 0.5);
INSERT INTO PRODUCTS VALUES ('P208', 'Microphone', 'USB microphone, desktop', 'Audio', 22.95, 2.5);
INSERT INTO PRODUCTS VALUES ('P210', 'Speakers', '2-speaker, stereo, 10W', 'Audio', 39.99, 7.5);
INSERT INTO PRODUCTS VALUES ('P010', 'Classic Keyboard', 'Black, spill resistant design', 'Input', 21.50, null);
INSERT INTO PRODUCTS VALUES ('P302', 'Inkjet Printer', 'Color and B/W modes, wireless support', 'Print', 89.99, null);
INSERT INTO PRODUCTS VALUES ('P304', 'Laser Printer', 'Color heavy-duty printer', 'Print', 119.50, 25);
INSERT INTO PRODUCTS VALUES ('P312', 'Letter Paper', 'Multipurpose 20lb, 500 sheets', 'Print', 7.50, 5);
INSERT INTO PRODUCTS VALUES ('P046', 'Screen cover', 'Dust protection unit', 'Display', 12.50, null);
INSERT INTO PRODUCTS VALUES ('P215', 'Speakers', 'Mono output, 5W', 'Audio', null, 5.5);
INSERT INTO PRODUCTS VALUES ('P235', 'Audio Pak', 'Speakers and Microphone', 'Audio', 35.95, 10);
INSERT INTO PRODUCTS VALUES ('P322', 'Printer Ink', 'Replacement Cartridges', 'Print', 30, 4);


INSERT INTO SALES VALUES('AX014','01-Mar-2017','Check',  'C002');
INSERT INTO SALES VALUES('CQ951','03-Oct-2016','Cash',   'C005');
INSERT INTO SALES VALUES('BC001','18-Feb-2017','Credit', 'C003');
INSERT INTO SALES VALUES('CB714','21-Sep-2014','PayPal', 'C101');
INSERT INTO SALES VALUES('BM701','04-Mar-2017','GWallet','C002');
INSERT INTO SALES VALUES('LC294','04-Apr-2015','Credit', 'C005');
INSERT INTO SALES VALUES('MB720','04-Oct-2015','PayPal', 'C104');

INSERT INTO SALESLINES VALUES ('AX014','P010',3,19.35);
INSERT INTO SALESLINES VALUES ('AX014','P012',2,14.75);
INSERT INTO SALESLINES VALUES ('AX014','P312',2,7.5);
INSERT INTO SALESLINES VALUES ('AX014','P011',10,40);
INSERT INTO SALESLINES VALUES ('CQ951','P011',4,54.3);
INSERT INTO SALESLINES VALUES ('CQ951','P046',50,11.25);
INSERT INTO SALESLINES VALUES ('BC001','P011',4,40.73);
INSERT INTO SALESLINES VALUES ('BC001','P074',4,8.99);
INSERT INTO SALESLINES VALUES ('BC001','P046',3,12.5);
INSERT INTO SALESLINES VALUES ('BC001','P322',5,30);
INSERT INTO SALESLINES VALUES ('CB714','P011',5,45.25);
INSERT INTO SALESLINES VALUES ('CB714','P302',3,89.99);
INSERT INTO SALESLINES VALUES ('MB720','P011',5,45);
INSERT INTO SALESLINES VALUES ('MB720','P302',3,90);
INSERT INTO SALESLINES VALUES ('BM701','P208',3,32.13);
INSERT INTO SALESLINES VALUES ('LC294','P051',1,103.46);
INSERT INTO SALESLINES VALUES ('LC294','P302',3,89.99);
INSERT INTO SALESLINES VALUES ('LC294','P235',5,43.14);
INSERT INTO SALESLINES VALUES ('LC294','P322',2,33);
INSERT INTO SALESLINES VALUES ('LC294','P312',4,6.75);
INSERT INTO SALESLINES VALUES ('LC294','P010',3,23.65);
INSERT INTO SALESLINES VALUES ('LC294','P074',4,13.99);



commit;

对于至少有两个订单的产品(总体,不考虑付款方式),显示产品 ID、产品名称、以现金支付的销售订单数量(标题:Num Cash Sales)、总体销售订单数量(标题:总销售额)

我在下面写了查询

select sl.prodid, p.pName, a.NumCashSales,count(sl.prodid) as NumOverallSales 
from 
(select count(saleno) as  NumCashSales, saleno as salesno 
    from sales where paymentmethod = 'Cash'
) a 
    Right Outer join saleslines sl 
    Join PRODUCTS p
on sl.prodid = p.prodid 
on sl.saleno= a.salesno

group by sl.prodid 
having count(sl.prodid) >=2; 

它在 mysql 上运行,但在 oracle 上却出现类似错误

ORA-00979: 不是 GROUP BY 表达式 00979. 00000 - “不是 GROUP BY 表达式” *原因: *行动: 行错误:1 列:19

谁能帮忙。

【问题讨论】:

您的查询不起作用,因为在 Oracle(在此遵循 SQL 中的标准)中,从文档“包含 GROUP BY 子句的查询不能引用选择列表中的非聚合列未在 GROUP BY 子句中命名”。这意味着您必须按所有非聚合字段(sl.prodid、p.pName、a.NumCashSales)分组。确实看看这个***.com/questions/21679804/… 你能不能给我这个查询的oracle版本 你也应该在“开启”条件 sl.prodid = p.prodid 和 sl.saleno= a.salesno 之间放置和 @CarmineTambascia - 接近但没有雪茄。该建议将修复旧式 ANSI 逗号连接,但不能修复 ANSI-92 显式连接。 谢谢。还在努力写一篇。没有运气。 【参考方案1】:

Oracle 的 Group by 实现是标准的:

在标准 SQL 中,包含 GROUP BY 子句的查询不能引用 到选择列表中未在 GROUP BY 子句。例如,此查询在标准 SQL 中是非法的 因为选择列表中的名称列没有出现在 分组依据:

为了使查询合法,必须从 在 GROUP BY 子句中选择列表或命名。

MySQL 扩展了 GROUP BY 的使用,以便选择列表可以引用 未在 GROUP BY 子句中命名的非聚合列。这表示 前面的查询在 MySQL 中是合法的。您可以使用此功能 通过避免不必要的列排序来获得更好的性能和 分组。但是,这主要在每个 未在 GROUP BY 中命名的非聚合列对于每个都相同 组。

所以这应该有效

select sl.prodid, p.pName, a.NumCashSales,count(sl.prodid) as 
  NumOverallSales 
from 
  (select count(saleno) as  NumCashSales, saleno as salesno 
     from sales where paymentmethod = 'Cash'
  ) a 
  Right Outer join saleslines sl 
     on sl.saleno= a.salesno
  Join PRODUCTS p  
     on sl.prodid = p.prodid 

 group by sl.prodid,p.pName, a.NumCashSales 
 having count(sl.prodid) >=2; 

【讨论】:

按 sl.prodid、p.pName、a.NumCashSales 分组 * 第 11 行出现错误:ORA-00905:缺少关键字 像上面那样出去。缺少关键字 您的查询有 两个 语法错误。第一个是 GROUP BY 错误,第二个是 ON 条件必须紧跟在它们所属的 JOIN 之后。我已经编辑了 Carmine 的解决方案来修复第二个。 @APC 感谢实际上我没有意识到这一点。我不好,但谢谢 Aditi,虽然我错过了连接条件,但感谢 APC 查询是您所要求的,即使有解释,非常感谢您将其作为答案【参考方案2】:

您按产品 ID 分组并希望同时显示产品名称。这在 MySQL 中有效,因此在 SQL 标准中定义。产品名称由产品 ID 唯一标识,因此没有任何问题。但是,Oracle 也要求您按产品名称进行分组。可能是这种情况,因为在某些情况下(不是这里)很难确定属性之间的功能依赖关系。

您的第一个子查询无效。您选择记录计数的一行(为了便于阅读,应该是count(*) 而不是count(saleno)),但也选择salesno。但是,salesno 可以有多个,因此您可以随意选择一个。这在 MySQL 中是可能的,但它实际上是一个缺陷。在最近的 MySQL 版本中,如果你想这样做,你必须使用ANY_VALUE(saleno)。在 Oracle 中这是无效的,因为它应该符合 SQL 标准。 Oracle 没有ANY_VALUE,但您可以改用MIN(saleno)MAX(saleno)。但是,也许您更愿意按 saleno 进行分组,以便每个 saleno 获得一行?

如果只有一个 a 记录,Oracle 将再次要求您将 NumCashSales 放入 GROUP BY。对于每个 saleno 一个 a 记录,您甚至必须应用一些聚合,例如SUM(a.NumCashSales) 每个产品。

那么您的联接不正确。它们必须紧跟在 ON 子句之后,而您的查询不是这种情况。 MySQL 让我们忽略这一点,并在此处违反 SQL 标准。

因此,首先要弄清楚您的原始查询。然后将pName 添加到GROUP BY 以遵守Oracle 的限制,您就完成了。

【讨论】:

谢谢!能否请您分享查询。 先尝试一下 :-) 您的主要问题是您当前的查询计算所有现金销售,但任意取一个现金 saleno 及其产品。你真的想要这个吗?如果没有,你想要什么? 是的。我要它。我正在尝试,但没有运气。 您想随机挑选一个saleno并显示它的数据吗?真的吗?这似乎没有多大意义。 我还是不会写查询【参考方案3】:

您认为您的问题是从 MySQL 到 Oracle 的查询转换失败。你错了。您的问题是您的查询有缺陷(Oracle 主要告诉您,MySQL 应该首先完成的操作)。好吧,诚然,有些问题 DBMS 无法告诉您,例如订购的件数不是订单中的行数。

这里是查询。这很简单。您加入所有表并聚合。只是,为了获得现金销售,您需要条件聚合。不需要外部连接。不需要子查询。

select
  p.prodid,
  p.pname,
  sum(sl.qty) as num_overall_sales,
  sum(case when s.paymentmethod = 'Cash' then sl.qty end) as num_cash_sales
from sales s 
join saleslines sl on sl.saleno = s.saleno
join products p on p.prodid = sl.prodid 
group by p.prodid, p.pname
having count(distinct s.saleno) >= 2;

此查询是标准 SQL,应该适用于几乎所有 RDBMS。它适用于 MySQL,也适用于 Oracle。

正如我在其他答案中提到的,GROUP BY p.pname 只是为了 Oracle 的缘故而出现在查询中; MySQL 和 SQL 标准都没有要求。

【讨论】:

以上是关于我的sql查询到oracle查询的转换的主要内容,如果未能解决你的问题,请参考以下文章

如何将 MS-SQL Server SELECT 查询转换/迁移到 Oracle 和 MySQL?

将 SQL 查询转换为 PL/SQL 可以提高 Oracle 12c 中的性能吗? [关闭]

Oracle查询转换之连接谓词推入

使用 LIKE 将 Oracle 查询转换为 SQL Server 查询

将 DB2 查询转换为 oracle 查询

Oracle 数据透视转换到 SQL 服务器