Bisakah saya membuat view dengan parameter di MySQL?


93

Saya memiliki pandangan seperti ini:

CREATE VIEW MyView AS
   SELECT Column FROM Table WHERE Value = 2;

Saya ingin membuatnya lebih umum, artinya mengubah 2 menjadi variabel. Saya mencoba ini:

CREATE VIEW MyView AS
   SELECT Column FROM Table WHERE Value = @MyVariable;

Tetapi MySQL tidak mengizinkan ini.

Saya menemukan solusi yang jelek:

CREATE FUNCTION GetMyVariable() RETURNS INTEGER DETERMINISTIC NO SQL
BEGIN RETURN @MyVariable; END|

Dan pandangannya adalah:

CREATE VIEW MyView AS
   SELECT Column FROM Table WHERE Value = GetMyVariable();

Tapi kelihatannya sangat jelek, dan penggunaannya juga jelek - Saya harus menyetel @MyVariable sebelum setiap penggunaan tampilan.

Apakah ada solusi, yang bisa saya gunakan seperti ini:

SELECT Column FROM MyView(2) WHERE (...)

Situasi konkretnya adalah sebagai berikut: Saya memiliki tabel yang menyimpan informasi tentang permintaan yang ditolak:

CREATE TABLE Denial
(
    Id INTEGER UNSIGNED AUTO_INCREMENT,
        PRIMARY KEY(Id),
    DateTime DATETIME NOT NULL,
    FeatureId MEDIUMINT UNSIGNED NOT NULL,
        FOREIGN KEY (FeatureId)
            REFERENCES Feature (Id)
            ON UPDATE CASCADE ON DELETE RESTRICT,
    UserHostId MEDIUMINT UNSIGNED NOT NULL,
        FOREIGN KEY (UserHostId)
            REFERENCES UserHost (Id)
            ON UPDATE CASCADE ON DELETE RESTRICT,
    Multiplicity MEDIUMINT UNSIGNED NOT NULL DEFAULT 1,
    UNIQUE INDEX DenialIndex (FeatureId, DateTime, UserHostId)
) ENGINE = InnoDB;

Multiplisitas adalah sejumlah permintaan identik yang direkam dalam detik yang sama. Saya ingin menampilkan daftar penolakan, tetapi terkadang, ketika aplikasi ditolak, ia mencoba lagi beberapa kali hanya untuk memastikan. Jadi biasanya, ketika pengguna yang sama mendapat penolakan 3 kali pada fitur yang sama dalam beberapa detik, itu sebenarnya adalah satu penolakan. Jika kita memiliki satu sumber daya lagi, untuk memenuhi permintaan ini, dua penolakan berikutnya tidak akan terjadi. Jadi kami ingin mengelompokkan penolakan dalam laporan yang memungkinkan pengguna untuk menentukan rentang waktu di mana penolakan harus dikelompokkan. Misalnya jika kita memiliki penolakan (untuk pengguna 1 pada fitur 1) di cap waktu: 1,2,24,26,27,45 dan pengguna ingin mengelompokkan penolakan yang lebih dekat satu sama lain dari 4 detik, dia harus mendapatkan sesuatu seperti ini: 1 (x2), 24 (x3), 45 (x1). Kita dapat berasumsi, bahwa ruang antara penyangkalan nyata jauh lebih besar daripada di antara duplikasi.

CREATE FUNCTION GetDenialMergingTime()
    RETURNS INTEGER UNSIGNED
    DETERMINISTIC NO SQL
BEGIN
    IF ISNULL(@DenialMergingTime) THEN
        RETURN 0;
    ELSE
        RETURN @DenialMergingTime;
    END IF;
END|

CREATE VIEW MergedDenialsViewHelper AS
    SELECT MIN(Second.DateTime) AS GroupTime,
        First.FeatureId,
        First.UserHostId,
        SUM(Second.Multiplicity) AS MultiplicitySum
    FROM Denial AS First 
        JOIN Denial AS Second 
            ON First.FeatureId = Second.FeatureId
                AND First.UserHostId = Second.UserHostId
                AND First.DateTime >= Second.DateTime
                AND First.DateTime - Second.DateTime < GetDenialMergingTime()
    GROUP BY First.DateTime, First.FeatureId, First.UserHostId, First.Licenses;

CREATE VIEW MergedDenials AS
    SELECT GroupTime, 
        FeatureId,
        UserHostId, 
        MAX(MultiplicitySum) AS MultiplicitySum
    FROM MergedDenialsViewHelper
    GROUP BY GroupTime, FeatureId, UserHostId;

Kemudian untuk menampilkan penolakan dari pengguna 1 dan 2 pada fitur 3 dan 4 yang digabungkan setiap 5 detik, yang harus Anda lakukan adalah:

SET @DenialMergingTime := 5;
SELECT GroupTime, FeatureId, UserHostId, MultiplicitySum FROM MergedDenials WHERE UserHostId IN (1, 2) AND FeatureId IN (3, 4);

Saya menggunakan view karena di dalamnya mudah untuk memfilter data dan menggunakannya secara eksplisit di grid jQuery, otomatis memesan, membatasi jumlah record, dan sebagainya.

Tapi itu hanya solusi yang buruk. Apakah ada cara yang tepat untuk melakukan ini?

Jawaban:


161

Sebenarnya jika Anda membuat func:

create function p1() returns INTEGER DETERMINISTIC NO SQL return @p1;

dan lihat:

create view h_parm as
select * from sw_hardware_big where unit_id = p1() ;

Kemudian Anda dapat memanggil tampilan dengan parameter:

select s.* from (select @p1:=12 p) parm , h_parm s;

Saya harap ini membantu.


31
Wow, ini adalah salah satu hal paling hack yang pernah saya lihat di SQL;) Tapi itulah yang ingin saya lakukan.
ssobczak

2
Teknik ini bekerja saat membuat tampilan dalam prosedur tersimpan, saat tampilan yang dibuat bergantung pada varchar yang diteruskan ke prosedur tersimpan. Dalam hal ini, saya harus 'menyetel @ p1 = 12;' di telepon sebelum panggilan membuat tampilan.
Clayton Stanley

2
Apakah ada potensi masalah (kekacauan data penyewa) jika beberapa penyewa database memanggil kode ini secara bersamaan?
Gruber

2
@Mr_and_and tabel turunan membutuhkan alias. Anda dapat menyebutnya apa pun yang Anda inginkan, tetapi Anda tidak dapat mengabaikannya
Robin Kanters

4
Variabel p1 mempertahankan nilainya setelah ini, jadi jika Anda menggunakan tampilan lagi tanpa meneruskan parameter, itu akan menggunakan yang sebelumnya diteruskan - yang mungkin membingungkan! Anda dapat "menghapus" setelah digunakan seperti ini: pilih s. * From (pilih p1: = 12 p) pass, h_parm s, (pilih @ p1: = - 1) clear; (Dengan asumsi -1 adalah nilai yang tidak valid untuk tujuan ini)
BuvinJ

21
CREATE VIEW MyView AS
   SELECT Column, Value FROM Table;


SELECT Column FROM MyView WHERE Value = 1;

Merupakan solusi yang tepat di MySQL, beberapa SQL lain memungkinkan Anda mendefinisikan Tampilan dengan lebih tepat.

Catatan: Kecuali jika Tampilannya sangat rumit, MySQL akan mengoptimalkannya dengan baik.


1
Dalam kasus saya, bagian WHERE, di mana saya ingin menggunakan parameter tidak dipilih, jadi tidak mungkin untuk memfilternya dari luar tampilan.
ssobczak

Sebenarnya pemilihan yang tidak tepat tidak diizinkan dalam tampilan, tetapi saya membaginya menjadi dua tampilan. V1 menyaring dan mengumpulkan data, dan di atas V1 ada V2. Saya tidak dapat memfilter data dari V1 di luarnya (di V2), karena di luar data tersebut terlihat sebagai agregat.
ssobczak

2
Maka jangan gunakan tampilan sama sekali, jika Anda membutuhkan kontrol yang tepat, buat seluruh kueri setiap saat, atau buat kueri di dalam prosedur tersimpan. Menyimpan sebagai tampilan tampaknya tidak ada gunanya. Meskipun jika Anda memposting pertanyaan yang Anda coba capai, seseorang mungkin dapat menyarankan rute yang berbeda / lebih baik.
MindStalker

Saya tidak ingin melakukan ini, karena ini akan membuat pertanyaan sederhana saya menjadi cukup rumit, tetapi jika menurut Anda ini mungkin berguna, saya akan mencoba.
ssobczak

Format ini tidak akan membiarkan Anda mengubah set hasil atau nama tabel tergantung pada parameter
MMEL

1

Saya sebelumnya menemukan solusi berbeda yang tidak menggunakan prosedur tersimpan, tetapi menggunakan tabel parameter dan beberapa sihir connection_id ().

EDIT (Disalin dari komentar)

buat tabel yang berisi kolom bernama connection_id(buatlah menjadi bigint). Tempatkan kolom di tabel itu untuk parameter tampilan. Letakkan kunci utama di connection_id. ganti ke dalam tabel parameter dan gunakan CONNECTION_ID()untuk mengisi nilai connection_id. Dalam tampilan, gunakan gabungan silang ke tabel parameter dan put WHERE param_table.connection_id = CONNECTION_ID(). Ini akan menggabungkan silang dengan hanya satu baris dari tabel parameter yang Anda inginkan. Anda kemudian dapat menggunakan kolom lain di klausa where misalnya where orders.order_id = param_table.order_id.


5
Yang mana? Tolong beritahu kami lebih dari itu.
marzapower

1
buat tabel yang berisi kolom bernama connection_id (buatlah menjadi bigint). Tempatkan kolom di tabel itu untuk parameter tampilan. Letakkan kunci utama di connection_id. ganti ke dalam tabel parameter dan gunakan CONNECTION_ID () untuk mengisi nilai connection_id. Dalam tampilan, gunakan gabungan silang ke tabel parameter dan letakkan WHERE param_table.connection_id = CONNECTION_ID (). Ini akan menggabungkan silang dengan hanya satu baris dari tabel parameter yang Anda inginkan. Anda kemudian dapat menggunakan kolom lain di klausa where misalnya where orders.order_id = param_table.order_id.
Justin Swanhart

KLUDGE! Tapi imut.
Rick James
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.