Menghitung persentase baris dari jumlah total

13

Permintaan maaf untuk gelar yang buruk, saya tidak yakin apa yang akan menjadi judul yang bagus untuk ini.

Ini saat ini (tampilan disederhanakan) data yang saya kerjakan

Agent    |  Commission     
---------|------------
Smith    |    100
Neo      |    200
Morpheus |    300

Saya perlu menghitung persentase dari total komisi, masing-masing agen bertanggung jawab untuk itu.

Jadi, untuk Agen Smith, Persentase akan dihitung sebagai (Agent Smith's commission / Sum(commission)*100

Jadi, data saya yang diharapkan

Agent    |  Commission   |  % Commission    
---------|---------------|---------------
Smith    |    100        |     17
Neo      |    200        |     33
Morpheus |    300        |     50

Saya memiliki fungsi mengembalikan komisi untuk setiap agen. Saya memiliki fungsi lain mengembalikan persentase sebagai (Commission/Sum(Commission))*100. Masalahnya adalah bahwa Sum(commission)akan dihitung untuk setiap baris, dan mengingat bahwa kueri ini akan dijalankan pada Gudang Data, kumpulan data akan lebih besar (saat ini, hanya di bawah 2.000 catatan) dan cukup jujur, pendekatan yang buruk (IMO ).

Apakah ada cara agar Sum(Commission)tidak menghitung untuk setiap baris yang diambil?

Saya sedang memikirkan sesuatu pada baris permintaan 2 bagian, bagian pertama akan mengambilnya sum(commission)menjadi variabel paket / tipe dan bagian kedua akan merujuk pada nilai pra-perhitungan ini, tapi saya tidak yakin bagaimana saya bisa mencapai ini.

Saya terbatas menggunakan SQL, dan saya menjalankan Oracle 10g R2.

Sathyajith Bhat
sumber
Tidak jelas pertanyaan DBA (mungkin jika itu tablespace bukan salesman?) - mungkin harus di Stack Overflow.
Gayus

Jawaban:

23

Anda sedang mencari analytical function ratio_to_report

select 
  agent,
  round(ratio_to_report(commission) over ()*100) "% Comm."
from  
  commissions;
René Nyffenegger
sumber
Luar biasa, tidak tahu tentang ini, terima kasih!
Sathyajith Bhat
9

Untuk mengembalikan semua agen dengan persentase komisi dan komisi mereka menggunakan fungsi analitik tanpa klausa analitik sehingga partisi berada di atas seluruh tabel:

SELECT Agent, commission, 100* commission / (SUM(commission) OVER ()) "% Commission" 
FROM commissions;

Seperti yang saya pelajari dari René Nyffenegger (+1) fungsi ratio_to_report mengencangkan sintaks ini.

Menggunakan paket untuk menyimpan Komisi SUM akan melibatkan PL / SQL, yang secara khusus Anda kecualikan dengan menunjukkan bahwa Anda menginginkan solusi SQL, tetapi karena Anda sudah menggunakan fungsi, saya berasumsi niat Anda bukan untuk mengecualikan PL / SQL. Jika ini masalahnya, maka solusi paket mungkin membantu, tetapi itu tergantung pada cara kerja aplikasi Anda.

Ketika sesi Anda pertama kali dibuat dan memanggil fungsi dalam paket untuk mendapatkan komisi, ada panggilan implisit ke konstruktor paket yang bisa mendapatkan jumlah dan menyimpannya. Kemudian Anda bisa mereferensikan jumlah yang disimpan dalam fungsi komisi Anda dan hanya perlu melakukan jumlah sekali. Tentu saja begitu Anda memanggil fungsi dari sesi yang berbeda, jumlahnya akan dihitung lagi. Juga, memanggil fungsi untuk setiap agen akan jauh lebih efisien daripada memanggil satu pernyataan SQL untuk semua agen jika aplikasi Anda dapat dirancang dengan cara itu.

Anda mungkin ingin mempertimbangkan untuk mengubah fungsi Anda menjadi prosedur yang mengembalikan kursor untuk kueri di atas atau mungkin memiliki fungsi yang mengembalikan hasil kueri sebagai hasil pipelined yang ditetapkan.

Contoh data:

create table commissions (Agent Varchar2(100), Commission Number(3));
insert into commissions values ('Smith',100);
insert into commissions values ('Neo',200);
insert into commissions values ('Morpheus',300);
Leigh Riffel
sumber
5

Anda dapat mencoba kueri berikut, jumlah (komisi) hanya akan dihitung sekali:

WITH TOTAL_COMMISSION AS 
(SELECT SUM(COMMISSION) AS TOTAL FROM AGENTS)
SELECT A.AGENT_NAME, A.COMMISSION, ((A.COMMISSION/T.TOTAL)*100) AS "% COMMISSION"
FROM AGENTS A, TOTAL_COMMISSION T;
Robert Durgin
sumber
Itu bekerja dan mengembalikan data yang benar, tetapi kurang efisien daripada fungsi analitik yang melakukan satu pemindaian tabel penuh daripada dua (dengan asumsi tidak ada indeks).
Leigh Riffel
1
@ Ligh ~ Bagaimana bisa melakukannya dalam satu pass karena cara manual membutuhkan dua pass? Saya tidak dapat melihat bagaimana komputer dapat membuat% dari total operasi satu arah yang ajaib ...
jcolebrand
@ jcolebrand Data hanya dibaca dari blok basis data satu kali. Mungkin melakukan beberapa lintasan dalam hasil memori, tetapi ini umumnya lebih cepat daripada membaca blok database dua kali. Ada pengorbanan dalam memori dan CPU antara opsi-opsi ini, jadi pilihannya mungkin tidak selalu jelas, tetapi dalam kasus ini saya pikir itu.
Leigh Riffel
1
@Leigh ~~ Ya, pertimbangan lebih lanjut akan membuat saya percaya bahwa hanya itu yang bisa dilakukan, hanya optimasi kotak hitam yang dikuatkan. Bagaimanapun, solusi bagus dalam jawaban Anda. Terima kasih: D
jcolebrand
0
  select 
  Agent, Commission,
  (
      ROUND(
       (Commission *100) / 
          (
            (SELECT SUM(Commission)
             FROM commissions AS A)
          )
       ) 
  ) AS Porcentaje
  from  
  commissions
JoeDeg
sumber