2015-08-29 20 views
8

Vì vậy, tôi đang tạo một hệ thống sẽ kéo 50-150 bản ghi tại một thời điểm từ một bảng và hiển thị chúng cho người dùng và tôi đang cố gắng để giữ số lượt xem cho mỗi bản ghi.SQL INSERT INTO SELECT và trả về dữ liệu SELECT để tạo số lượt xem hàng

Tôi đã tìm ra cách hiệu quả nhất là tạo bảng MEMORY mà tôi sử dụng INSERT INTO để kéo ID của các hàng vào và sau đó có hàm cron chạy thường xuyên để tổng số lượt xem ID và xóa bảng bộ nhớ, cập nhật bảng gốc với số lượt xem mới nhất. Điều này tránh liên tục cập nhật bảng có khả năng sẽ được truy cập nhiều nhất, vì vậy tôi không khóa 150 hàng tại một thời điểm với mỗi truy vấn (hoặc toàn bộ bảng nếu tôi đang sử dụng MyISAM).

Về cơ bản, phương pháp được giải thích here. Tuy nhiên, dĩ nhiên tôi muốn làm điều này cùng lúc khi tôi lấy thông tin hồ sơ để xem và tôi muốn tránh chạy một truy vấn thứ hai, riêng biệt chỉ để lấy cùng một tập dữ liệu cho nó đếm.

Có cách nào để chọn tập dữ liệu, trả lại tập dữ liệu đó và đồng thời chèn một cột đơn từ tập dữ liệu đó vào bảng khác không?

Có vẻ như PostgreSQL có thể có thứ gì đó tương tự như những gì tôi muốn với từ khóa RETURNING, nhưng tôi đang sử dụng MySQL.

+0

INSERT INTO mytable (col1, col2, col3 ...) CHỌN col1, col2, col3 TỪ anotherTable – bksi

+0

Liệu truy vấn đó sẽ trả về tất cả các cột từ câu lệnh SELECT cuối cùng?Tôi muốn chỉ chèn cột ID vào bảng khác, và sau đó quay trở lại PHP tất cả các cột khác của cùng một câu lệnh SELECT. Đó có phải là cách INSERT INTO hoạt động không? – Ecksters

+2

Không thể với MySQL. Bạn sẽ phải sử dụng hai truy vấn. – rjdown

Trả lời

4

Trước hết, tôi sẽ không thêm một cột truy cập vào bảng Main. Tôi sẽ tạo một bảng Audit riêng biệt có thể giữ ID mục từ bảng Main cộng với ít nhất dấu thời gian khi yêu cầu ID đó. Về bản chất, bảng Audit sẽ lưu trữ lịch sử yêu cầu. Trong phương pháp này, bạn có thể dễ dàng tạo ra nhiều báo cáo thú vị hơn. Bạn luôn luôn có thể tính toán tổng số lớn cho mỗi mục và bạn cũng có thể tính toán tóm tắt theo ngày, tuần, tháng, vv cho mỗi mục hoặc trên tất cả các mục. Tùy thuộc vào khối lượng dữ liệu bạn có thể xóa các mục kiểm tra định kỳ cũ hơn một số ngưỡng (một tháng, một năm, v.v ...).

Ngoài ra, bạn có thể dễ dàng lưu trữ thêm thông tin trong bảng Audit nếu cần, ví dụ: ID người dùng để tính số liệu thống kê cho mỗi người dùng.

Để điền Audit bảng "tự động", tôi sẽ tạo một quy trình được lưu trữ. Mã máy khách sẽ gọi thủ tục lưu sẵn này thay vì thực hiện SELECT gốc. Thủ tục được lưu trữ sẽ trả về chính xác kết quả tương tự như mã gốc SELECT, nhưng cũng sẽ thêm các chi tiết cần thiết vào bảng Audit một cách minh bạch vào mã máy khách.

Vì vậy, chúng ta hãy giả sử rằng Audit bảng trông như thế này:

CREATE TABLE AuditTable 
(
    ID int 
    IDENTITY -- SQL Server 
    SERIAL -- Postgres 
    AUTO_INCREMENT -- MySQL 
    NOT NULL, 
    ItemID int NOT NULL, 
    RequestDateTime datetime NOT NULL 
) 

và chính bạn SELECT trông như thế này:

SELECT ItemID, Col1, Col2, ... 
FROM MainTable 
WHERE <complex criteria> 

Thực hiện cả hai INSERTSELECT trong một tuyên bố trong SQL Server tôi 'd sử dụng mệnh đề OUTPUT, trong mệnh đề Postgres - RETURNING, trong MySQL - ??? Tôi không nghĩ rằng nó có bất cứ điều gì như thế này. Vì vậy, thủ tục MySQL sẽ có một số câu lệnh riêng biệt.

MySQL

Lúc đầu làm SELECT của bạn và chèn kết quả vào một temporary (có thể nhớ) bảng. Sau đó sao chép ID mục từ bảng tạm thời vào bảng Audit. Sau đó, SELECT từ bảng tạm thời để trả lại kết quả cho khách hàng.

CREATE TEMPORARY TABLE TempTable 
(
    ItemID int NOT NULL, 
    Col1 ..., 
    Col2 ..., 
    ... 
) 
ENGINE = MEMORY 
SELECT ItemID, Col1, Col2, ... 
FROM MainTable 
WHERE <complex criteria> 
; 

INSERT INTO AuditTable (ItemID, RequestDateTime) 
SELECT ItemID, NOW() 
FROM TempTable; 

SELECT ItemID, Col1, Col2, ... 
FROM TempTable 
ORDER BY ...; 

SQL Server (chỉ để trêu chọc bạn. Câu lệnh này hiện cả hai INSERTSELECT)

MERGE INTO AuditTable 
USING 
(
    SELECT ItemID, Col1, Col2, ... 
    FROM MainTable 
    WHERE <complex criteria> 
) AS Src 
ON 1 = 0 
WHEN NOT MATCHED BY TARGET THEN 
INSERT 
    (ItemID, RequestDateTime) 
VALUES 
    (Src.ItemID, GETDATE()) 
OUTPUT 
    Src.ItemID, Src.Col1, Src.Col2, ... 
; 

Bạn có thể để lại Audit bảng như nó có, hoặc bạn có thể thiết lập cron để tóm tắt nó định kỳ. Nó thực sự phụ thuộc vào khối lượng dữ liệu. Trong hệ thống của chúng tôi, chúng tôi lưu trữ các hàng riêng lẻ trong một tuần, cộng với chúng tôi tóm tắt số liệu thống kê mỗi giờ và giữ nó trong 6 tuần, cộng với chúng tôi giữ bản tóm tắt hàng ngày trong 18 tháng. Nhưng, phần quan trọng, tất cả các tóm tắt này là các bảng riêng biệt, chúng tôi không giữ thông tin kiểm tra trong bảng Main, vì vậy chúng tôi không cần phải cập nhật nó.

Joe Celko giải thích nó rất tốt trong SQL Style Habits: Attack of the Skeuomorphs:

Bây giờ đi đến bất kỳ văn bản SQL diễn đàn tìm kiếm các thông tin đăng. Bạn sẽ tìm thấy hàng nghìn bài đăng có DDL bao gồm các cột có tên là createdby, createddate, modifiedbymodifieddate với dữ liệu meta cụ thể đó ở cuối hàng. Đây là nhãn băng cũ được viết bằng ngôn ngữ mới! Deja Vu!

Bản ghi tiêu đề chỉ xuất hiện một lần trên băng. Nhưng các giá trị dữ liệu meta này xuất hiện lặp đi lặp lại trên mỗi hàng trong bảng. Một trong những lý do chính của để sử dụng cơ sở dữ liệu (không chỉ SQL) là loại bỏ dự phòng khỏi dữ liệu; điều này chỉ làm tăng thêm sự thừa. Nhưng bây giờ hãy suy nghĩ về điều gì xảy ra với đường mòn kiểm toán khi một hàng bị xóa? Điều gì sẽ xảy ra với đường mòn kiểm tra khi một hàng được cập nhật? Đường mòn bị phá hủy. Dữ liệu kiểm tra phải được tách riêng khỏi lược đồ. Bạn có đặt tệp nhật ký trên cùng một ổ đĩa làm cơ sở dữ liệu không? Kế toán viên có cho phép cùng một người chấp nhận và nhận thanh toán không?

+0

Câu trả lời tuyệt vời! Tôi tìm thấy ý tưởng về các thông tin bổ sung được sắp xếp thành nhiều bảng rất thú vị, vui khi bạn nhìn ở trên và vượt ra ngoài các thông số ban đầu. Tôi sẽ chờ đợi một thời gian dài để xem liệu có ai có bất kỳ câu trả lời nào mà họ coi là đáng nói hay không, nhưng có khả năng nó sẽ lấy tiền thưởng :) – Ecksters

+0

Bạn được chào đón. Tôi lấy cảm hứng từ bài viết này (https://www.simple-talk.com/opinion/opinion-pieces/sql-style-habits-attack-of-the-skeuomorphs/) của Joe Celko. Tôi sẽ thêm một báo giá có liên quan cho câu trả lời. –

+0

Đó là một câu trả lời hay, nhưng tôi sẽ bao gồm thông tin nhận dạng người dùng (tức là người yêu cầu) trong dữ liệu kiểm toán. Trong khi điều này được đề cập trong phần nội dung của câu trả lời ở trên, tôi khuyên bạn nên làm điều này từ ngày 1 (nb. Bạn không thể quay ngược thời gian để tìm ra ai đã yêu cầu thông tin). –

3

Bạn đang yêu cầu nếu MySQL hỗ trợ trình kích hoạt SELECT. Nó không. Bạn sẽ cần phải làm điều này như hai truy vấn, tuy nhiên bạn có thể gắn chúng vào trong một thủ tục được lưu trữ - sau đó bạn có thể vượt qua trong phạm vi bạn đang tìm nạp, có cả hai trả về kết quả VÀ làm INSERT vào bảng khác.

câu trả lời Cập nhật với ví dụ khung cho thủ tục lưu trữ:

DELIMITER $$ 
CREATE PROCEDURE `FetchRows`(IN StartID INT, IN EndID INT) 
BEGIN 
    UPDATE Blah SET ViewCount = ViewCount+1 WHERE id >= StartID AND id <= EndID; 
    #^Assumes counts are stored in the same table. If they're in a seperate table, do an INSERT INTO ... ON DUPLICATE KEY UPDATE ViewCount = ViewCount+1 instead. 
    SELECT * FROM Blah WHERE id >= StartID AND id <= EndID; 
END$$ 
DELIMITER ; 
+1

Tính gọn gàng, về cơ bản phản ứng cốt lõi ở đây như xa như tôi có thể nói. – Ecksters

+0

Vâng, xin lỗi tôi đã không thể trả lời "ồ đúng rồi, có một tính năng ít được biết đến này thực hiện chính xác những gì bạn muốn" :(Thật đáng ngạc nhiên, có một cái gì đó như thế này trong PostgreSQL tôi nghĩ, "UPDATE ... RETURNING". http://www.postgresql.org/docs/9.1/interactive/sql-update.html –

+0

Ồ đúng rồi, và tôi đã chỉnh sửa câu trả lời của mình để cung cấp một ví dụ về quy trình lưu trữ bộ xương để thực hiện những gì cả tôi và Vladimir đề xuất./cập nhật trở thành 'CALL FetchRows (1, 7);' hoặc bất cứ điều gì. –

Các vấn đề liên quan