2010-01-06 26 views
5

PLATFORM MY:PHP & mySQL: mục Lặp đi lặp lại vấn đề - Cần mỗi mục để hiển thị lên chỉ một lần nhưng họ lặp lại

PHP & mySQL

GÌ TÔI CÓ TẠI ĐÂY:

tôi có 4 bảng, cụ thể là 'sách', 'book_type', 'book_categories', 'all_categories'.

GÌ Tôi đang cố gắng làm:

Nói cách đơn giản, tôi muốn hiển thị tất cả các sách có trong kho tức in_stock = 'y', với tất cả các thông tin cuốn sách liên quan đến từ tất cả các bảng, chỉ một lần mà không lặp lại các mục. Hiện tại mỗi cuốn sách được lặp lại và tôi chỉ muốn hiển thị chúng một lần.

CÁC HIỆN VẤN ĐỀ:.

Trong frontend trong ứng dụng của tôi, các mục được hiển thị liên tục trong khi thực tế khi tôi đang chờ đợi họ xuất hiện chỉ một lần (như trong DISTINCT/UNIQUE) và không lặp lại chính họ.

MY nghi ngờ:

tôi nghi ngờ rằng các dữ liệu lặp lại là vì trong các loại mà mỗi người trong số những cuốn sách thuộc về. Mỗi mục nhập sách được hiển thị nhiều lần vì nó thuộc về một danh mục. Gây nhầm lẫn? Tôi có nghĩa là nếu một book1 thuộc về 4 loại, thì book1 được hiển thị 4 lần. Nếu book2 thuộc về 2 loại, thì nó được hiển thị 2 lần.

GÌ TÔI CÓ CẦN:

Tôi cần PHP & mySQL đang rằng sẽ giải quyết vấn đề trên. Tôi hy vọng rằng chúng tôi có thể giải quyết vấn đề mà không sử dụng GROUP_CONCAT trong mySQL vì có giới hạn (1024?) Cho cùng một vấn đề. Sách có thể thuộc nhiều danh mục và tôi không muốn mạo hiểm mất bất kỳ dữ liệu nào bằng cách sử dụng GROUP_CONCAT. Tôi cũng muốn làm điều này trong một truy vấn duy nhất mà không cần truy cập cơ sở dữ liệu liên tục trong một vòng lặp. Cảm ơn vì đã hiểu.

Tất cả các bảng và dữ liệu tương ứng để nhân rộng các vấn đề như sau:

CREATE TABLE IF NOT EXISTS `books` (
    `book_id` int(11) NOT NULL auto_increment, 
    `book_type_id` int(11) NOT NULL, 
    `book_title` varchar(50) NOT NULL, 
    `book_price` smallint(4) NOT NULL, 
    `in_stock` char(1) NOT NULL, 
    PRIMARY KEY (`book_id`), 
    KEY `book_type_id` (`book_type_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; 

-- 
-- Dumping data for table `books` 
-- 

INSERT INTO `books` (`book_id`, `book_type_id`, `book_title`, `book_price`, `in_stock`) VALUES 
(1, 1, 'My Book 1', 10, 'y'), 
(2, 1, 'My Book 2', 20, 'n'), 
(3, 2, 'My Book 3', 30, 'y'), 
(4, 3, 'My Book 4', 40, 'y'), 
(5, 2, 'My Book 5', 50, 'n'), 
(6, 1, 'My Book 6', 60, 'y'), 
(7, 3, 'My Book 7', 70, 'n'), 
(8, 2, 'My Book 8', 80, 'n'), 
(9, 1, 'My Book 9', 90, 'y'), 
(10, 3, 'My Book 10', 100, 'n'); 

-- 
-- Table structure for table `book_type` 
-- 

CREATE TABLE IF NOT EXISTS `book_type` (
    `book_type_id` int(11) NOT NULL auto_increment, 
    `book_type` varchar(50) NOT NULL, 
    PRIMARY KEY (`book_type_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; 

-- 
-- Dumping data for table `book_type` 
-- 

INSERT INTO `book_type` (`book_type_id`, `book_type`) VALUES 
(1, 'Good'), 
(2, 'Better'), 
(3, 'Best'); 


-- 
-- Table structure for table `book_categories` 
-- 

CREATE TABLE IF NOT EXISTS `book_categories` (
    `book_id` int(11) NOT NULL, 
    `cat_id` int(11) NOT NULL, 
    PRIMARY KEY (`book_id`,`cat_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

-- 
-- Dumping data for table `book_categories` 
-- 

INSERT INTO `book_categories` (`book_id`, `cat_id`) VALUES 
(1, 1), 
(1, 2), 
(1, 3), 
(1, 4), 
(1, 5), 
(2, 1), 
(2, 2), 
(3, 1), 
(3, 2), 
(3, 3); 


-- 
-- Table structure for table `all_categories` 
-- 

CREATE TABLE IF NOT EXISTS `all_categories` (
    `cat_id` int(11) NOT NULL auto_increment, 
    `category` varchar(50) NOT NULL, 
    PRIMARY KEY (`cat_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; 

-- 
-- Dumping data for table `all_categories` 
-- 

INSERT INTO `all_categories` (`cat_id`, `category`) VALUES 
(1, 'Comedy'), 
(2, 'Drama'), 
(3, 'Romance'), 
(4, 'Horror'), 
(5, 'Trivia'), 
(6, 'Puzzles'), 
(7, 'Riddles'), 
(8, 'Kids'), 
(9, 'Gents'), 
(10, 'Ladies'); 

MY MỤC TIÊU:

//MY QUERY: 
SELECT books.book_title, books.book_price, 
     book_type.book_type, 
     all_categories.category 
FROM books 
LEFT JOIN book_type  ON books.book_type_id = book_type.book_type_id 
LEFT JOIN book_categories ON books.book_id = book_categories.book_id 
LEFT JOIN all_categories ON book_categories.cat_id = all_categories.cat_id 
WHERE books.in_stock = 'y' 

HIỆN OUTPUT:

book_title book_price book_type  category 
My Book 1 10   Good   Comedy 
My Book 1 10   Good   Drama 
My Book 1 10   Good   Romance 
My Book 1 10   Good   Horror 
My Book 1 10   Good   Trivia 
My Book 3 30   Better   Comedy 
My Book 3 30   Better   Drama 
My Book 3 30   Better   Romance 
My Book 4 40   Best   NULL 
My Book 6 60   Good   NULL 
My Book 9 90   Good   NULL 

CẦN đầu ra sau đây:

book_title book_price book_type  category 
My Book 1 10   Good   Comedy, Drama, Romance, Horror, Trivia 
My Book 3 30   Better   Comedy, Drama, Romance 
My Book 4 40   Best   NULL 
My Book 6 60   Good   NULL 
My Book 9 90   Good   NULL 

Nhờ tất cả trước.

+0

Lưu ý chỉnh sửa của tôi liên quan đến 100 nhận xét truy vấn của bạn. – MindStalker

+0

SELECT book.book_id, all_categories.category TỪ book_category THAM GIA all_categories trên book_categories.cat_id = all_categories.cat_id THAM GIA sách trên books.book_id = book_categories.book_id WHERE books.in_stock = 'y'; – MindStalker

+0

HOẶC CHỌN book_categories.book_id, all_categories.category TỪ book_category THAM GIA all_categories trên book_categories.cat_id = all_categories.cat_id WHERE book_id IN (SELECT book_id FROM books WHERE books.in_stock = 'y'); – MindStalker

Trả lời

3

Cách tốt nhất để chắc chắn rằng bạn không bị mất bất kỳ dữ liệu sẽ được truy vấn nhiều. Truy vấn các bảng riêng rẽ và tham gia cùng họ trong PHP, có lẽ vì vậy truy vấn của bạn trông như thế này

book_id book_title book_price book_type   
    1 My Book 1 10   Good   
    2 My Book 3 30   Better   
    3 My Book 4 40   Best   
    4 My Book 6 60   Good    
    5 My Book 9 90   Good   

    book_id, category 
    1 Comedy 
    1 Drama 
    1 Romance 
    2 Comedy 

vv

Edit:

Không, bạn không nên cần 100 hits trên DB, chỉ hai , một để có được những cuốn sách, tiếp theo để có được các loại. Vòng lặp sẽ được thực hiện trong PHP để lặp qua truy vấn thứ hai và kết hợp dữ liệu với đầu tiên. Truy vấn thứ hai có thể là

CHỌN book.book_id, all_categories.category FROM book_category THAM GIA all_categories trên book_categories.cat_id = all_categories.cat_id THAM GIA sách trên books.book_id = book_categories.book_id WHERE books.in_stock = 'y';

HOẶC

SELECT book_categories.book_id, all_categories.category FROM book_category 
JOIN all_categories on book_categories.cat_id=all_categories.cat_id 
WHERE book_id IN (SELECT book_id FROM books WHERE books.in_stock= 'y'); 
+0

Cảm ơn bạn đã trả lời. Điều đó có nghĩa là ngoài truy vấn nhận dữ liệu của tôi, đó là một vòng lặp bổ sung tìm nạp các danh mục cho mỗi người trong số họ. Điều đó có nghĩa là nếu tôi có 100 cuốn sách trong kho, thì chỉ để có được các danh mục, đó là 100 lượt truy cập bổ sung trên DB (1 lần truy cập/truy vấn/sách). Tôi cảm thấy rằng rất nhiều lượt truy cập từ nhiều người dùng sẽ làm giảm nghiêm trọng trang web. Điều đó có ý nghĩa? Xin vui lòng cho tôi biết suy nghĩ của bạn về điều này. – Devner

+0

Làm cho nó hoạt động ngay bây giờ, tối ưu hóa sau –

+0

@ d03boy Ok, bây giờ làm thế nào để tối ưu hóa nó? Cảm ơn. – Devner

1

Bạn thân, bạn nên xem GROUP_CONCAT() chức năng của MySQL. http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat

Nó sẽ là một cái gì đó giữa các dòng:

SELECT stuff, GROUP_CONCAT(DISTINCT category ORDER BY category DESC SEPARATOR ', ') 
GROUP BY category, price, book_type 
+0

Xin chào Clash. Chỉ cho phép tối đa 1024 BYTES cho GROUP_CONCAT. Vì vậy, điều đó có nghĩa nếu chiều dài (category + separator) là> 1024 bytes, thì truy vấn sẽ thất bại. Tôi có đúng không? Như tôi đã đề cập, một cuốn sách có thể thuộc về nhiều loại. Nói trung bình, Danh mục dài 15 byte. Bây giờ nếu cuốn sách thuộc về 100 Danh mục, thì không gian được lấy là 1500 byte cao hơn 1024. Điều này sẽ dẫn đến kết quả không chính xác. Tôi đã biết về GROUP_CONCAT và muốn tránh điều đó vì cùng một lý do. Xin vui lòng cho tôi biết suy nghĩ của bạn. Cảm ơn. – Devner

+0

Có cài đặt 'group_concat_max_len' (mặc định là 1024), có thể bạn có thể đặt giá trị cao hơn? – Clash

+0

Hi Clash, tôi không quá chắc chắn nếu điều đó có thể được thực hiện thông qua các kịch bản. Tôi nghĩ rằng có thể liên quan đến những người lưu trữ web, những người (theo như tôi biết) không muốn thay đổi bất cứ điều gì. Hầu hết thời gian, đồng nghiệp của tôi báo cáo rằng bất kỳ thay đổi nào đối với cài đặt máy chủ đều bị từ chối rất thường xuyên vì máy chủ web có xu hướng tránh lạm dụng bằng cách thay đổi cài đặt theo nhu cầu của người dùng. Vì vậy, tôi nghĩ đến việc giải quyết nó mà không cần sự hỗ trợ của họ. Tôi nghĩ rằng nếu chúng ta có thể có được một giải pháp hoàn hảo cho điều này, thì chúng ta không cần phải lo lắng về những người lưu trữ một lần nữa, liên quan đến một vấn đề tương tự. Hy vọng rằng có ý nghĩa. – Devner

1

Bạn cũng có thể sử dụng seperator CONCAT_WS với một subquery, một cái gì đó như:

SELECT books.book_title, 
    books.book_price, 
    book_type.book_type, 
    CONCAT_WS(', ', ( 
         SELECT all_categories.category 
          FROM all_categories 
         WHERE all_categories.cat_id = book_categories.cat_id 
        ) 
      ) AS book_category_list 
FROM books 
LEFT JOIN book_type 
     ON books.book_type_id = book_type.book_type_id     
LEFT JOIN book_categories 
     ON books.book_id = book_categories.book_id 
    WHERE books.in_stock = 'y' 

Xin lỗi, định dạng có một chút kỳ lạ đó.

+0

Hi Skone. Bạn có cơ hội thử truy vấn của mình một cách thiết thực không? Tôi đã làm và nó vẫn mang lại cho tôi những kết quả tương tự như trong CURRENT OUTPUT với ngoại lệ là kết quả từ truy vấn của bạn KHÔNG hiển thị các giá trị NULL. Nó vẫn lặp lại những hàng mà tôi muốn tránh. Ngoài ra điều gì sẽ là giới hạn cho CONCAT_WS? Giới hạn của GROUP_CONCAT là 1024 byte, do đó bạn có ý tưởng về các giới hạn của CONCAT_WS không? Làm ơn cho tôi biết. Cảm ơn. – Devner

+0

Tôi đã không thực sự thử nó - xin lỗi vì nó không hoạt động như mong đợi. Tôi hơi bận vào lúc này nhưng tôi sẽ cố gắng trả lời sau tối nay với câu trả lời (không hứa hẹn): P Chúc mừng. – Skone

+0

Cảm ơn và đánh giá cao những nỗ lực của bạn. – Devner

1

Rất đơn giản. Thêm nhóm theo. Vì vậy, nó sẽ là như vậy:

EDIT: Có vẻ như cách tốt nhất để làm điều này là sử dụng concat nhóm (mặc dù bạn lo ngại về giới hạn trên) để vượt qua giới hạn trên nó có tôi sẽ đề nghị cập nhật nó tại thời gian chạy. Vì vậy, nó sẽ là một cái gì đó như thế này:

SET GLOBAL group_concat_max_len = SOME_BIG_VALUE; #SOME_BIG_VALUE >> 1024 

SELECT books.book_title, books.book_price, 
     book_type.book_type, 
     GROUP_CONCAT(all_categories.category) 
FROM books 
LEFT JOIN book_type  ON books.book_type_id = book_type.book_type_id 
LEFT JOIN book_categories ON books.book_id = book_categories.book_id 
LEFT JOIN all_categories ON book_categories.cat_id = all_categories.cat_id 
WHERE books.in_stock = 'y' 
GROUP BY books.book_title 
+0

Xin chào, truy vấn mà bạn đã cung cấp chỉ tìm nạp Danh mục đầu tiên cho tên sách 'Sách của tôi 1' và 'Sách của tôi 3'. Đối với phần còn lại của sách (Sách của tôi 4, 6 & 9), danh mục là NULL. Đơn giản chỉ cần đặt, mặc dù các hàng đã ngừng lặp lại, tất cả các loại không được lấy ra. Chỉ loại đầu tiên là. Nếu bạn có thể sửa đổi truy vấn của mình để nó hiển thị tất cả các danh mục, tôi tin rằng sẽ giải quyết được vấn đề. Cảm ơn. – Devner

+0

Xin lỗi tôi đã chỉnh sửa điều này trước khi tôi đọc nhận xét của bạn về việc phải đi qua công ty lưu trữ. –

+0

+1 để mở rộng mã GROUP_CONCAT – Devner

0

Bạn đã thử thêm riêng biệt cho lựa chọn của mình chưa?

SELECT DISTINCT books.book_title, books.book_price, 
    book_type.book_type, 
    all_categories.category 
FROM books 
LEFT JOIN book_type  ON books.book_type_id = book_type.book_type_id 
LEFT JOIN book_categories ON books.book_id = book_categories.book_id 
LEFT JOIN all_categories ON book_categories.cat_id = all_categories.cat_id 
WHERE books.in_stock = 'y' 
GROUP BY books.book_title 
+0

Cảm ơn bạn đã nhận xét, Jim. Kết quả truy vấn của bạn giống như kết quả của truy vấn do WillMatt cung cấp. Vui lòng tham khảo bình luận của tôi về câu trả lời của anh ấy. Cảm ơn một lần nữa. – Devner

1

Đặt phiên biến đến một giá trị lớn, chạy truy vấn của bạn với GROUP_CONCAT, sau đó thiết lập lại nó trở lại với giá trị toàn cầu.

SET SESSION [email protected]@max_allowed_packet; 

SELECT books.book_title, books.book_price, 
     book_type.book_type, 
     GROUP_CONCAT(all_categories.category) 
FROM books 
LEFT JOIN book_type  ON books.book_type_id = book_type.book_type_id 
LEFT JOIN book_categories ON books.book_id = book_categories.book_id 
LEFT JOIN all_categories ON book_categories.cat_id = all_categories.cat_id 
WHERE books.in_stock = 'y' 
GROUP BY books.book_id; 

SET SESSION [email protected]@group_concat_max_len; 
+0

+1 cho mẹo. Cảm ơn bạn. – Devner

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