SORU
21 AĞUSTOS 2009, Cuma


Son kayda alınıyor her grupta

Aşağıda gösterildiği gibi veri içeren bir tablo messages:

Id   Name   Other_Columns
-------------------------
1    A       A_data_1
2    A       A_data_2
3    A       A_data_3
4    B       B_data_1
5    B       B_data_2
6    C       C_data_1

Eğer bir sorgu çalıştırırsanız select * from messages group by name sonuç olarak alacağım:

1    A       A_data_1
4    B       B_data_1
6    C       C_data_1

Ne sorgu aşağıdaki sonuç verecektir?

3    A       A_data_3
5    B       B_data_2
6    C       C_data_1

Diğer bir deyişle, her grubun son kaydı iade edilmelidir.

Şu anda bu kullandığım sorgu

select * from (select * from messages ORDER BY id DESC) AS x GROUP BY name

Ama bu son derece verimsiz görünüyor. Aynı sonuca ulaşmak için başka yollar?

CEVAP
21 AĞUSTOS 2009, Cuma


Çözüm bu şekilde yazıyorum:

SELECT m1.*
FROM messages m1 LEFT JOIN messages m2
 ON (m1.name = m2.name AND m1.id < m2.id)
WHERE m2.id IS NULL;

Performansı, bir çözüm veya başka daha iyi, verilerin yapısına bağlı olabilir. Her iki sorgu test ve veritabanı verilen performans en iyi olanı kullanmalısınız.

Örneğin, StackOverflow Ağustos veri dökümü bir kopyası var. Kıyaslama için kullanacağım. Posts tablo 1,114,357 satır vardır. Bu 2.40 GHz Pro benim Macbook üzerinde MySQL 5.0.75 üzerinde çalışıyor.

Sorgusu, belirli bir kullanıcı KİMLİĞİ (benim) en son post bulmak için yazacağım.

İlk teknik shown @tarafından Eric sorgu GROUP BY ile kullanma:

SELECT p1.postid
FROM Posts p1
INNER JOIN (SELECT pi.owneruserid, MAX(pi.postid) AS maxpostid
            FROM Posts pi GROUP BY pi.owneruserid) p2
  ON (p1.postid = p2.maxpostid)
WHERE p1.owneruserid = 20860;

1 row in set (1 min 17.89 sec)

EXPLAIN bile analizi üzerinde 16 saniye sürer:

 ---- ------------- ------------ -------- ---------------------------- ------------- --------- -------------- --------- ------------- 
| id | select_type | table      | type   | possible_keys              | key         | key_len | ref          | rows    | Extra       |
 ---- ------------- ------------ -------- ---------------------------- ------------- --------- -------------- --------- ------------- 
|  1 | PRIMARY     | <derived2> | ALL    | NULL                       | NULL        | NULL    | NULL         |   76756 |             | 
|  1 | PRIMARY     | p1         | eq_ref | PRIMARY,PostId,OwnerUserId | PRIMARY     | 8       | p2.maxpostid |       1 | Using where | 
|  2 | DERIVED     | pi         | index  | NULL                       | OwnerUserId | 8       | NULL         | 1151268 | Using index | 
 ---- ------------- ------------ -------- ---------------------------- ------------- --------- -------------- --------- ------------- 
3 rows in set (16.09 sec)

Şimdi üretmek aynı sorgu sonucu LEFT JOIN ile benim tekniği:

SELECT p1.postid
FROM Posts p1 LEFT JOIN posts p2
  ON (p1.owneruserid = p2.owneruserid AND p1.postid < p2.postid)
WHERE p2.postid IS NULL AND p1.owneruserid = 20860;

1 row in set (0.28 sec)

EXPLAIN analiz her iki tablonun dizinlerini kullanmak mümkün olduğunu gösterir:

 ---- ------------- ------- ------ ---------------------------- ------------- --------- ------- ------ -------------------------------------- 
| id | select_type | table | type | possible_keys              | key         | key_len | ref   | rows | Extra                                |
 ---- ------------- ------- ------ ---------------------------- ------------- --------- ------- ------ -------------------------------------- 
|  1 | SIMPLE      | p1    | ref  | OwnerUserId                | OwnerUserId | 8       | const | 1384 | Using index                          | 
|  1 | SIMPLE      | p2    | ref  | PRIMARY,PostId,OwnerUserId | OwnerUserId | 8       | const | 1384 | Using where; Using index; Not exists | 
 ---- ------------- ------- ------ ---------------------------- ------------- --------- ------- ------ -------------------------------------- 
2 rows in set (0.00 sec)

İşte Posts masam için DDL:

CREATE TABLE `posts` (
  `PostId` bigint(20) unsigned NOT NULL auto_increment,
  `PostTypeId` bigint(20) unsigned NOT NULL,
  `AcceptedAnswerId` bigint(20) unsigned default NULL,
  `ParentId` bigint(20) unsigned default NULL,
  `CreationDate` datetime NOT NULL,
  `Score` int(11) NOT NULL default '0',
  `ViewCount` int(11) NOT NULL default '0',
  `Body` text NOT NULL,
  `OwnerUserId` bigint(20) unsigned NOT NULL,
  `OwnerDisplayName` varchar(40) default NULL,
  `LastEditorUserId` bigint(20) unsigned default NULL,
  `LastEditDate` datetime default NULL,
  `LastActivityDate` datetime default NULL,
  `Title` varchar(250) NOT NULL default '',
  `Tags` varchar(150) NOT NULL default '',
  `AnswerCount` int(11) NOT NULL default '0',
  `CommentCount` int(11) NOT NULL default '0',
  `FavoriteCount` int(11) NOT NULL default '0',
  `ClosedDate` datetime default NULL,
  PRIMARY KEY  (`PostId`),
  UNIQUE KEY `PostId` (`PostId`),
  KEY `PostTypeId` (`PostTypeId`),
  KEY `AcceptedAnswerId` (`AcceptedAnswerId`),
  KEY `OwnerUserId` (`OwnerUserId`),
  KEY `LastEditorUserId` (`LastEditorUserId`),
  KEY `ParentId` (`ParentId`),
  CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`PostTypeId`) REFERENCES `posttypes` (`PostTypeId`)
) ENGINE=InnoDB;

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Besnik Ibrahimi

    Besnik Ibrah

    27 Mart 2010
  • The Exploiteers

    The Exploite

    4 Ocak 2011
  • Willie D.

    Willie D.

    16 Aralık 2006