• Das Erstellen neuer Accounts wurde ausgesetzt. Bei berechtigtem Interesse bitte Kontaktaufnahme über die üblichen Wege. Beste Grüße der Admin

Neusten Eintrag mit mehreren Bedingungen

Cuthbert

New member
Sehr geehrte Leser!

Im Moment hänge ich an einer eher simplen MySQL Problematik. Ich habe eine Tabelle, die wie folgt aussieht:

id - user_id - pfp - yea - mon
1 - 5 ------- 20 - 2010 - 03
2 - 6 ------- 22 - 2010 - 03
3 - 7 ------- 23 - 2010 - 03
4 - 8 ------- 25 - 2010 - 03
5 - 9 ------- 28 - 2010 - 03
6 - 5 ------- 30 - 2010 - 04
7 - 7 ------- 33 - 2010 - 04
8 - 8 ------- 32 - 2010 - 04
9 - 9 ------- 37 - 2010 - 04

[Auszug, ist natürlich für unendliche viele Benutzer und unendliche viele yea (Jahr), mon (Monat), user_id (Benutzer) möglich.]

Das Problem: Ich habe ein aktuelles Monat gegeben, bspw 2010 04:
1) ich möchte nun die Benutzer auslesen, die in diesem Monat einen Eintrag haben.
2) Sofern der Benutzer in diesem Monat keinen Eintrag hat, soll ein vorhergehender gewählt werden (bspw. 03 oder früher!)
3) Es werden aber nicht alle Benutzer abgerufen, sondern nur bestimmte Benutzer [bspw: WHERE user_id IN (1,2,3)]

Also kurz: Zu jedem einzelnen User in der IN-Bedingung soll die maximale Jahr-Monat-Kombination mit korrespondierendem "pfp"-Wert herausgegeben. Für jeden Benutzer natürlich nur 1 Wert.

Result-Beispiel für Monat 201004 und user_id IN (5,6,7):
id - user_id - pfp - yea - mon
6 - 5 ------- 30 - 2010 - 04
2 - 6 ------- 22 - 2010 - 03
7 - 7 ------- 33 - 2010 - 04

Hatte schon diverse Lösungsansätze, aber ich möchte hier niemanden auf die falsche Fährte führen. Ich stecke leider wirklich gerade, also bitte ich euch um Hilfe.

Schöne Grüße
 
Ist (oder könnte) user_id, year, month primary key bzw unique sein, sprich für jeder Benutzer hat maximale einen Eintrag pro Monat-Jahr-Kombination?
Wenn ja wäre die Anfrage doch:
Wähle zu den Benutzern aus x den pfp Wert des jüngsten Datums aus.
Wenn das soweit stimmt, solltest du unbedingt yea und mon (warum überhaupt diese Verstümmelung?) in einer Spalte speichern. Entweder als unix_timestamp oder als date-typ. Macht die Abfrage einfacher:

Code:
SELECT *
FROM `table` as outer_table
WHERE `date` = 
    (SELECT max(`inner_table`.`date`) 
          FROM `table` 
          WHERE `inner_table`.`user_id` = `outer_table`.`user_id`)
AND `user_id` in (...)
Sollte es nicht gehen, oder Syntaxfehler auftreten, bitte ich um nen dump deiner Datenbank
 
Dankeschön!

id ist primary_key
user_id, yea, mon sind in dieser Kombination als unique eingetragen und können daher auch nur 1 mal in einer best. Kombination vorkommen.

Konnte es heute nur noch kurz antesten, hat (zumindest in diesem kurzen Test) nicht geklappt. Hier ein Export aus der Tabelle (sind alles nur "Testwerte", es existieren noch keine tatsächlichen Werte für diese Tabelle):

PHP:
-- phpMyAdmin SQL Dump
-- version 2.11.9.6
-- http://www.phpmyadmin.net
--
-- Host: 127.0.0.3
-- Erstellungszeit: 22. April 2010 um 17:50
-- Server Version: 5.1.39
-- PHP-Version: 4.4.8

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

-- --------------------------------------------------------

--
-- Tabellenstruktur für Tabelle `user_status`
--

CREATE TABLE `user_status` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `pfp` int(2) NOT NULL,
  `mon` int(2) NOT NULL,
  `yea` int(4) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id` (`user_id`,`mon`,`yea`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 ;

--
-- Daten für Tabelle `user_status`
--

INSERT INTO `user_status` (`id`, `user_id`, `pfp`, `mon`, `yea`) VALUES
(1, 1, 29, 3, 2010),
(2, 2, 30, 3, 2010),
(3, 3, 25, 3, 2010),
(4, 4, 30, 3, 2010),
(5, 5, 33, 3, 2010),
(6, 6, 30, 3, 2010),
(7, 7, 27, 3, 2010),
(8, 8, 30, 3, 2010),
(9, 2, 29, 4, 2010),
(10, 3, 30, 4, 2010),
(11, 4, 33, 4, 2010),
(12, 5, 33, 4, 2010),
(13, 6, 33, 4, 2010),
(14, 7, 33, 4, 2010),
(15, 8, 33, 4, 2010);
 
Hab' mal den Gedankten vom Zeitgeist zuende gedacht:
Code:
SELECT *, concat(
	LPAD(CONVERT(`outer_table`.`yea`, CHAR), 4, '0'),
	'-',
	LPAD(CONVERT(`outer_table`.`mon`, CHAR), 2, '0')
) as `date`
FROM `user_status` as `outer_table`
HAVING `date` = (
	SELECT max(
		concat(
			LPAD(CONVERT(`inner_table`.`yea`, CHAR), 4, '0'),
			'-',
			LPAD(CONVERT(`inner_table`.`mon`, CHAR), 2, '0')
		)
	)
	FROM `user_status` as `inner_table`
	WHERE
		`outer_table`.`user_id` = `inner_table`.`user_id` AND
		concat(
			LPAD(CONVERT(`inner_table`.`yea`, CHAR), 4, '0'),
			'-',
			LPAD(CONVERT(`inner_table`.`mon`, CHAR), 2, '0')
		) <= '2010-04'
) AND `outer_table`.`user_id` in (1,5,6,7)
 
Spitze! Habs kurz angetestet - scheint zu funktionieren.

Habe persönlich eher gedacht es liese sich mit einer Kombination aus GROUP BY user_id, LIMIT und MAX(CONCAT(yea,mon)) "einfach" lösen. Aber eure Lösung trifft es wohl auf den Kopf (Für eine Lösung mit Limit müsste es etwas geben wie LIMIT BY user_id 0,1 - vllt kommt es ja in zukünftigen Versionen).

Dankeschön für die Hilfen!
 
Unbedingt eine Date-Spalte verwenden!!!
Den jeweiligen Monat oder das jeweilige Jahr aus dieser heraus zu holen ist einfacher als bei all den anderen Abfragen immer Monat und Jahr zusammen zunehmen. Für Monat und Jahr gibt es in MYSQL spezielle Funktionen, sowohl für die unix_timestamp Variante als auch für das Date-Format.
Das würde bei der Anfrage von kkapsner die Häflte der Zeilen sparen.

Worin lag denn mein Fehler? Oder passt die Anfrage nur nicht auf das Datenbankschema? (Komm gerade von ner Happy Hour und hab nen etwas eingeschränktes Verständnis, bitte um euer Verständnis :) )
 
@ZeitGeist: mit einer Datumsspalte wäre es VIEL einfacher.
Und bei deinem Ansatz ist mehreres nicht in Ordnung:
1. `inner_table` wird nicht definiert
2. es gibt keine Spalte `date`
3. `date` wird nicht nach oben hin beschränkt
4. aber der Ansatz mit dem MAX war nicht schlecht...
 
Unbedingt eine Date-Spalte verwenden!!!
Den jeweiligen Monat oder das jeweilige Jahr aus dieser heraus zu holen ist einfacher als bei all den anderen Abfragen immer Monat und Jahr zusammen zunehmen. Für Monat und Jahr gibt es in MYSQL spezielle Funktionen, sowohl für die unix_timestamp Variante als auch für das Date-Format.
Das würde bei der Anfrage von kkapsner die Häflte der Zeilen sparen.

Nun, ihr habt mich natürlich überzeugt. Ich habe selbst nur ziemlich unfreiwillig diese Einteilung gewählt und dabei nicht an dieses Problem gedacht. Ich bin einfach den unix_timestampt zu sehr gewohnt, dieser schien mir aber unpassend, obwohl es natürlich genauso funktionieren würde.

Habe aus yea(INT4) und mon(INT2) einfach ein ein date(DATE) gemacht. Die Abfrage hat sich dadurch tatsächlich stark verkürzt. Dankeschön!

PHP:
SELECT *
FROM `user_status` as `outer_table`
HAVING `date` = (
	SELECT max(`date`)
	FROM `user_status` as `inner_table`
	WHERE
		`outer_table`.`user_id` = `inner_table`.`user_id` &&
		`date`< '2010-04-00'
) && `outer_table`.`user_id` in (1,5,6,7)

Da ich mich nun das erste mal mit MySQL Datumsformaten beschäftige muss ich kurz fragen: eigene Formate sind nicht möglich? Da eigentlich nur die Jahr-Monat kombination gespeichert werden muss. Die Tage (sprich -00) wären eigentlich überflüssig.

Schönen Dank!
 
Zuletzt bearbeitet:
Das Having ist wahrscheinlich noch von meinem Code - da hab' ich's gebraucht.

Warum? Nen Where wäre da doch auch möglich.

[Edit]
Nen WHERE ist sogar sinnvoller:

#

The HAVING clause is applied nearly last, just before items are sent to the client, with no optimization. (LIMIT is applied after HAVING.)
[..]
Do not use HAVING for items that should be in the WHERE clause. For example, do not write the following:

SELECT col_name FROM tbl_name HAVING col_name > 0;

Write this instead:

SELECT col_name FROM tbl_name WHERE col_name > 0;
http://dev.mysql.com/doc/refman/5.1/en/select.html

@Cuthbert
du solltest also lieber WHERE benutzen, dass könnte die Abfrage beschleunigen.
Deine Frage verstehe ich nicht ganz. Es gibt kein Date-type ohne Tag. Du mußt jetzt halt von außen sicherstellen, dass immer der gleiche Wert für einen Tag verwendet wird.
 
Natürlich wäre ein WHERE besser - ABER dann könnte ich nicht mit `date` arbeiten... ist aber auch nicht nötig. Muss das ganze CONCAT-Dedöns in das WHERE...
 
Also die finale Version sieht dann so aus:

PHP:
SELECT *
FROM `user_status` as `outer_table`
WHERE `date` = (
    SELECT max(`date`)
    FROM `user_status` as `inner_table`
    WHERE
        `outer_table`.`user_id` = `inner_table`.`user_id` &&
        `date` <= '2010-04-00'
) && `outer_table`.`user_id` in (1,5,6,7)

Vielen Dank nochmals!
 
Zuletzt bearbeitet:
Mich irritiert jetzt noch das "< '2010-04-00' ", da das neuestes Datum 2010-03-31 zurückgibt - aber wenn du das so haben willst... bitte;)
 
Mich irritiert jetzt noch das "< '2010-04-00' ", da das neuestes Datum 2010-03-31 zurückgibt - aber wenn du das so haben willst... bitte;)
Oh, heißt natürlich eigentlich `date` <= '2010-04-00'.

Da nur Jahr und Monat wichtig sind, nehm ich die letzten beiden stellen als 00. Steht sowieso nur 2010-03-00, 2010-04-00... etc drinnen.

Schöne Grüße
 
Zurück
Oben