2016-05-26 2 views
8

Ich habe eine Tabelle wie folgt aus:Wie mache ich ein dynamisches Limit in MySQL?

// notifications 
+----+--------------+------+---------+------------+ 
| id |  event | seen | id_user | time_stamp | 
+----+--------------+------+---------+------------+ 
| 1 | vote   | 1 | 123  | 1464174617 | 
| 2 | comment  | 1 | 456  | 1464174664 | 
| 3 | vote   | 1 | 123  | 1464174725 | 
| 4 | answer  | 1 | 123  | 1464174813 | 
| 5 | comment  | NULL | 456  | 1464174928 | 
| 6 | comment  | 1 | 123  | 1464175114 | 
| 7 | vote   | NULL | 456  | 1464175317 | 
| 8 | answer  | NULL | 123  | 1464175279 | 
| 9 | vote   | NULL | 123  | 1464176618 | 
+----+--------------+------+---------+------------+ 

Ich versuche, mindestens 15 Zeilen für bestimmte Benutzer auszuwählen. Nur gibt es zwei Bedingungen:

  1. immer alle ungelesenen Zeilen (seen = NULL) sollte angepasst werden, auch wenn sie mehr als 15 Zeilen.

  2. Wenn die Anzahl der ungelesenen Zeilen mehr als 15 beträgt, sollte auch 2 gelesene Zeilen ausgewählt werden (seen = 1).


Beispiele:read ist die Anzahl der gelesenen Zeilen und unread ist die Anzahl der Zeilen in ungelesenen notifications Tabelle.

read | unread |   output should be   
------|--------|------------------------------------- 
3 | 8  | 11 rows        
12 | 5  | 15 rows (5 unread, 10 read)   
20 | 30  | 32 rows (30 unread, 2 read)   
10 | 0  | 10 rows (0 unread, 10 read)   
10 | 1  | 11 rows (1 unread, 10 read)   
10 | 6  | 15 rows (6 unread, 9 read)   
100 | 3  | 15 rows (3 unread, 12 read)   
3 | 100 | 102 rows (100 unread, 2 read)  

Hier ist meine aktuelle Abfrage, spielt es keine zweite Bedingung unterstützen.

SELECT id, event, seen, time_stamp 
FROM notifications n 
WHERE id_user = :id AND seen IS NULL 
) UNION 
(SELECT id, event, seen, time_stamp 
FROM notifications n 
WHERE id_user = :id 
ORDER BY (seen IS NULL) desc, time_stamp desc 
LIMIT 15 
) 
ORDER BY (seen IS NULL) desc, time_stamp desc; 
+2

Wählen Sie einfach alle unsichtbaren und (Vereinigung mit) 15 gesehen. Danach kürzen (clientseitig) auf 15 wenn dort weniger als 15 ungesehen sind. –

+0

Ihre zweite Unterabfrage vermisst 'gesehen ist nicht null 'Bedingung –

+0

@vp_arth Ihre Idee enthält einige hilfreiche Punkte. Danke. –

Antwort

1

ich eine Lösung finden. Um die zweite Bedingung hinzuzufügen (zwei gelesene Zeilen auswählen, wenn mehr als 15 ungelesene Zeilen vorhanden sind), muss ich einen weiteren verwenden UNION. Etwas wie dieses:

(SELECT id, event, seen, time_stamp 
FROM notifications n 
WHERE id_user = :id AND seen IS NULL 
)UNION 
(SELECT id, event, seen, time_stamp 
FROM notification n 
WHERE id_user = :id AND seen IS NOT NULL 
LIMIT 2 
)UNION 
(SELECT id, event, seen, time_stamp 
FROM notifications n 
WHERE id_user = :id 
ORDER BY (seen IS NULL) desc, time_stamp desc 
LIMIT 15 
) 
ORDER BY (seen IS NULL) desc, time_stamp desc; 

Die erste Unterabfrage erhält alle ungesehenen Reihen. Die zweite erhält zwei gesehene Zeilen. Der Dritte bekommt fünfzehn Zeilen. Die UNION entfernt Duplikate, aber keine andere Grenze wird angewendet.

+1

und jetzt kenne ich den Unterschied zwischen "union" und "union all" :) Danke. –

0

Ich würde vielleicht die Abfrage vereinfachen und eine Nachbearbeitung Logik in der Anwendung verwenden, um den Rand Fall zu behandeln 14 oder 15 Reihen um aufweisen, die ungelesenen sind. Wählen Sie einfach bis zu 17 statt 15 Zeilen aus und gehen Sie beim Durchlaufen der Ergebnismenge in Ihrer Client-Anwendung einfach nicht in die Zeilen 16 und 17, es sei denn, die Zeilen 14 und 15 sind ungelesen.

Diese Abfrage könnte so einfach sein wie:

SELECT id, event, seen, time_stamp 
FROM notifications n 
WHERE id_user = :id 
ORDER BY seen DESC, time_stamp DESC 
LIMIT 17 
+0

Danke für Ihre Mühe, aber ich glaube, Sie haben meinen Standpunkt nicht verstanden. 'LIMIT 17' begrenzt den Ausgang auf 17 Zeilen in max. Was passiert, wenn 20 ungelesene Zeilen vorhanden sind? Wie gesagt, ich möchte immer ** alle ** ungelesenen Zeilen auswählen. –

1
SELECT id, event, seen, time_stamp 
FROM notifications n 
WHERE id_user = 123 AND seen IS NULL 

UNION 

(SELECT id, event, seen, time_stamp 
FROM ( 
SELECT id, event, seen, n.id_user, time_stamp, un.CNT 
    FROM notifications n 
    JOIN (
     SELECT COUNT(1) CNT, id_user 
     FROM notifications 
     WHERE id_user = 123 and seen is NULL 
     group by id_user 
     ) un 
    ON n.id_user = un.id_user 
    WHERE CNT > 15 
) t1 
WHERE t1.SEEN is not NULL 
LIMIT 2) 

UNION 

SELECT id, event, seen, time_stamp 
FROM ( 
SELECT id, event, seen, n.id_user, time_stamp, un.CNT 
    FROM notifications n 
    JOIN (
     SELECT COUNT(1) CNT, id_user 
     FROM notifications 
     WHERE id_user = 123 and seen is NULL 
     group by id_user 
     ) un 
    ON n.id_user = un.id_user 
    WHERE CNT < 15 
) t1 
WHERE t1.SEEN is not NULL 
+0

Oh .. das ist eine ziemlich komplexe Abfrage. Ich weiß nicht, ob es funktioniert oder nicht, aber ich bevorzuge es nicht. Ich denke, es gibt einen einfacheren Ansatz. Danke trotzdem und +1 für deinen Versuch. –

1

einfach alle unsichtbaren und (Vereinigung mit) wählen 15 gesehen.

SELECT id, event, seen, time_stamp 
FROM notifications n 
WHERE id_user = :id AND seen IS NULL 
UNION ALL 
(SELECT id, event, seen, time_stamp 
FROM notifications n 
WHERE id_user = :id AND seen IS NOT NULL 
LIMIT 15) 

So haben Sie jetzt alle ungelesenen und bis zu 15 Lesebenachrichtigungen.

Danach können Sie (Client-Seite) auf 15 kürzen, wenn dort weniger als 15 ungesehen ist.

Der beste Ort für tun, denke ich, ist holen Schleife.
Zählen Sie einfach gesehen/ungesehen und brechen Sie die Schleife an der Stelle, an der Sie genug Reihen erreichen.

Einige Pseudo-Code php:

$read = $unread = 0; 

while($row = $db->fetch()) { 
    if ($row['seen']) $read++; 
    if (!$row['seen']) $unread++; 
    // ... 
    if ($weHaveEnoughRows) break; 
} 
+0

Danke .. ja kann richtig sein. +1. Kannst du mir bitte meine Antwort ansehen und mir deine Meinung dazu sagen? –

1

versuchen Sie bitte diese,

Tabelle T kehrt lesen Benachrichtigungen mit Zeilennummer, um von time_stamp ab.

Dann wählen Sie aus T, wo Zeile < = GREATEST (15-Count() von ungelesen, 2).

und dann Vereinigung alle mit ungelesenen

SELECT id,event,seen,time_stamp 
FROM 
    (SELECT id, event, seen, time_stamp,@row:[email protected]+1 as row 
    FROM notifications n,(SELECT @row := 0)r 
    WHERE id_user = :id AND seen IS NOT NULL 
    ORDER BY time_stamp desc 
    )T 
WHERE T.row <= GREATEST(15- 
        (SELECT COUNT(*) FROM notifications n 
        WHERE id_user = :id AND seen IS NULL),2) 
UNION ALL 
(SELECT id, event, seen, time_stamp 
FROM notifications n 
WHERE id_user = :id 
AND seen is NULL 
) 
ORDER BY (seen IS NULL) desc,time_stamp desc 
1

Versuchen:

SET @`id_user` := 123; 

SELECT `id`, `event`, `seen`, `time_stamp` 
FROM (SELECT `id`, `event`, `seen`, `time_stamp`, @`unread` := @`unread` + 1 
     FROM `notifications`, (SELECT @`unread` := 0) `unr` 
     WHERE `id_user` = @`id_user` AND `seen` IS NULL 
     UNION ALL 
     SELECT `id`, `event`, `seen`, `time_stamp`, @`read` := @`read` + 1 
     FROM `notifications`, (SELECT @`read` := 0) `r` 
     WHERE `id_user` = @`id_user` AND `seen` IS NOT NULL 
      AND (
       @`read` < (15 - @`unread`) OR 
       ((15 - @`unread`) < 0 AND @`read` < 2) 
      ) 
) `source`; 

SQL Fiddle demo

+0

Aber in deiner Geige gibt es 18 ungelesene Zeilen ('seen = null') und die Ausgabe ist 18 Zeilen' :-) '..! erwartetes Ergebnis ist 20 Zeilen. –

+0

Oh Entschuldigung, Sie haben Recht .. zwei dieser 18 ungelesenen Zeilen gehören einem anderen Benutzer. –

+0

Ihre Abfrage hat nur ein kleines Problem .. es hat keine Reihenfolge .. Ich möchte diese Reihenfolge: 'ORDER BY (gesehen IS NULL) desc, time_stamp desc', Können Sie es bitte zu Ihrer Abfrage hinzufügen? –