ฉันสามารถสร้างมุมมองด้วยพารามิเตอร์ใน MySQL ได้หรือไม่


93

ฉันมีมุมมองเช่นนี้:

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

ฉันต้องการทำให้มันกว้างขึ้นหมายถึงเปลี่ยน 2 เป็นตัวแปร ฉันลองสิ่งนี้:

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

แต่ MySQL ไม่อนุญาต

ฉันพบวิธีแก้ปัญหาที่น่าเกลียด:

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

จากนั้นมุมมองคือ:

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

แต่มันดูเส็งเคร็งจริงๆและการใช้งานก็เส็งเคร็งเช่นกัน - ฉันต้องตั้งค่า @MyVariable ก่อนการใช้งานมุมมองแต่ละครั้ง

มีวิธีแก้ไขไหมที่ฉันสามารถใช้เช่นนี้:

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

สถานการณ์ที่เป็นรูปธรรมมีดังนี้: ฉันมีตารางที่จัดเก็บข้อมูลเกี่ยวกับคำขอที่ถูกปฏิเสธ:

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;

หลายหลากคือจำนวนคำขอที่เหมือนกันซึ่งบันทึกไว้ในวินาทีเดียวกัน ฉันต้องการแสดงรายการการปฏิเสธ แต่บางครั้งเมื่อแอปพลิเคชันถูกปฏิเสธแอปพลิเคชันจะถูกปฏิเสธอีกสองสามครั้งเพื่อให้แน่ใจ ดังนั้นโดยปกติแล้วเมื่อผู้ใช้คนเดียวกันถูกปฏิเสธ 3 ครั้งในคุณสมบัติเดียวกันภายในสองสามวินาทีมันก็เป็นการปฏิเสธเพียงครั้งเดียว หากเรามีทรัพยากรอีกหนึ่งรายการเพื่อตอบสนองคำขอนี้การปฏิเสธสองครั้งถัดไปจะไม่เกิดขึ้น ดังนั้นเราจึงต้องการจัดกลุ่มการปฏิเสธในรายงานโดยให้ผู้ใช้ระบุช่วงเวลาที่ควรจัดกลุ่มการปฏิเสธ เช่นหากเรามีการปฏิเสธ (สำหรับผู้ใช้ 1 ในฟีเจอร์ 1) ในการประทับเวลา: 1,2,24,26,27,45 และผู้ใช้ต้องการจัดกลุ่มการปฏิเสธที่อยู่ใกล้กันมากกว่า 4 วินาทีเขาควรได้รับสิ่งนี้: 1 (x2), 24 (x3), 45 (x1) เราสามารถสันนิษฐานได้ว่าช่องว่างระหว่างการปฏิเสธจริงนั้นใหญ่กว่าระหว่างการทำซ้ำ

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;

จากนั้นเพื่อแสดงการปฏิเสธจากผู้ใช้ 1 และ 2 ในฟีเจอร์ 3 และ 4 ที่ผสานทุก 5 วินาทีสิ่งที่คุณต้องทำคือ:

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

ฉันใช้มุมมองนี้เพราะง่ายต่อการกรองข้อมูลและใช้อย่างชัดเจนในกริด jQuery เรียงลำดับอัตโนมัติ จำกัด จำนวนเรกคอร์ดและอื่น ๆ

แต่มันเป็นเพียงวิธีแก้ปัญหาที่น่าเกลียด มีวิธีที่เหมาะสมในการทำเช่นนี้หรือไม่?

คำตอบ:


162

จริงๆแล้วถ้าคุณสร้าง func:

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

และดู:

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

จากนั้นคุณสามารถเรียกดูด้วยพารามิเตอร์:

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

ฉันหวังว่ามันจะช่วยได้


31
ว้าวนี่เป็นหนึ่งในสิ่งที่แฮ็คที่สุดที่ฉันเคยเห็นใน SQL;) แต่มันคือสิ่งที่ฉันอยากทำ
ssobczak

2
เทคนิคนี้ใช้ได้ผลเมื่อสร้างมุมมองภายในโพรซีเดอร์ที่เก็บไว้เมื่อมุมมองที่สร้างขึ้นขึ้นอยู่กับ varchar ที่ส่งผ่านไปยังโพรซีเดอร์ที่เก็บไว้ ในกรณีนี้ฉันต้อง 'set @ p1 = 12;' ในบรรทัดก่อนการโทรเพื่อสร้างมุมมอง
Clayton Stanley

2
มีความเป็นไปได้ที่จะเกิดปัญหาหรือไม่ (การผสมข้อมูลผู้เช่า) หากผู้เช่าฐานข้อมูลหลายรายเรียกรหัสนี้พร้อมกัน
Gruber

2
@Mr_and_Mrs_D ตารางที่ได้มาจำเป็นต้องมีนามแฝง คุณสามารถเรียกมันว่าอะไรก็ได้ แต่คุณไม่สามารถละเว้นได้
Robin Kanters

4
ตัวแปร p1 ยังคงรักษาค่าไว้หลังจากนี้ดังนั้นหากคุณใช้มุมมองอีกครั้งโดยไม่ส่งผ่านพารามิเตอร์มันจะใช้ค่าที่ผ่านมาก่อนหน้าซึ่งอาจทำให้สับสน! คุณสามารถ "ล้าง" ได้หลังจากใช้เช่นนี้: select s. * from (select p1: = 12 p) pass, h_parm s, (select @ p1: = - 1) clear; (สมมติว่า -1 เป็นค่าที่ไม่ถูกต้องสำหรับวัตถุประสงค์นี้)
BuvinJ

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


SELECT Column FROM MyView WHERE Value = 1;

เป็นโซลูชันที่เหมาะสมใน MySQL SQL อื่น ๆ ช่วยให้คุณกำหนด Views ได้ตรงประเด็นมากขึ้น

หมายเหตุ: เว้นแต่มุมมองจะซับซ้อนมาก MySQL จะปรับให้เหมาะสม


1
ในกรณีของฉันส่วน WHERE ซึ่งฉันต้องการใช้พารามิเตอร์อยู่ใน neasted select ดังนั้นจึงเป็นไปไม่ได้ที่จะกรองจากภายนอกมุมมอง
ssobczak

ไม่อนุญาตให้มีการเลือกแบบ neasted ในมุมมอง แต่ฉันแบ่งออกเป็นสองมุมมอง V1 กรองและรวบรวมข้อมูลและที่ด้านบนของ V1 มี V2 ฉันไม่สามารถกรองข้อมูลจาก V1 ภายนอกได้ (ใน V2) เนื่องจากภายนอกจะมองเห็นเป็นข้อมูลรวม
ssobczak

2
จากนั้นอย่าใช้มุมมองเลยถ้าคุณต้องการการควบคุมที่แน่นอนจะสร้างแบบสอบถามทั้งหมดทุกครั้งหรือสร้างแบบสอบถามภายในกระบวนงานที่เก็บไว้ การบันทึกเป็นมุมมองดูเหมือนไม่มีจุดหมาย แม้ว่าคุณจะโพสต์คำถามที่คุณพยายามทำให้สำเร็จอาจมีคนแนะนำเส้นทางที่แตกต่าง / ดีกว่าได้
MindStalker

ฉันไม่ต้องการทำเช่นนี้เพราะมันจะทำให้คำถามง่ายๆของฉันค่อนข้างซับซ้อน แต่ถ้าคุณคิดว่ามันอาจมีประโยชน์ฉันจะลอง
ssobczak

รูปแบบนี้จะไม่อนุญาตให้คุณเปลี่ยนชุดผลลัพธ์หรือชื่อของตารางขึ้นอยู่กับพารามิเตอร์
MMEL

1

ก่อนหน้านี้ฉันได้คิดวิธีแก้ปัญหาอื่นที่ไม่ใช้กระบวนงานที่เก็บไว้ แต่ใช้ตารางพารามิเตอร์และเวทย์มนตร์ connection_id () แทน

แก้ไข (คัดลอกมาจากความคิดเห็น)

สร้างตารางที่มีคอลัมน์ที่เรียกว่า connection_id (ทำให้เป็น bigint) วางคอลัมน์ในตารางนั้นสำหรับพารามิเตอร์สำหรับมุมมอง ใส่คีย์หลักบนconnection_id. แทนที่ลงในตารางพารามิเตอร์และใช้CONNECTION_ID()เพื่อเติมค่า connection_id WHERE param_table.connection_id = CONNECTION_ID()ในการใช้งานมุมมองข้ามเข้าร่วมกับตารางพารามิเตอร์และใส่ สิ่งนี้จะข้ามการเข้าร่วมด้วยแถวเดียวจากตารางพารามิเตอร์ซึ่งเป็นสิ่งที่คุณต้องการ จากนั้นคุณสามารถใช้คอลัมน์อื่น ๆ orders.order_id = param_table.order_idในข้อที่ยกตัวอย่างเช่นที่


5
อันไหน? โปรดบอกอะไรเพิ่มเติม
marzapower

1
สร้างตารางที่มีคอลัมน์ชื่อ connection_id (ทำให้เป็น bigint) วางคอลัมน์ในตารางนั้นสำหรับพารามิเตอร์สำหรับมุมมอง ใส่คีย์หลักบน connection_id แทนที่ลงในตารางพารามิเตอร์และใช้ CONNECTION_ID () เพื่อเติมค่า connection_id ในมุมมองใช้การรวมข้ามกับตารางพารามิเตอร์และใส่ WHERE param_table.connection_id = CONNECTION_ID () สิ่งนี้จะข้ามการเข้าร่วมด้วยแถวเดียวจากตารางพารามิเตอร์ซึ่งเป็นสิ่งที่คุณต้องการ จากนั้นคุณสามารถใช้คอลัมน์อื่นใน where clause ตัวอย่างเช่น order.order_id = param_table.order_id
Justin Swanhart

คลูดจ์! แต่น่ารัก.
Rick James
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.