only full group by aneb, něco mi uteklo?

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
kleinpetr
Člen | 480
+
-1
-

Ahoj chci se zeptat co je špatného na tomto sql dotazu, který jsem normálně používal, ale najednou mi začal házet chybu, měnilo se něco v nette ? Díky za pomoc

SELECT *
FROM `table`
WHERE (DATE(`createdDate`) <= CURDATE() + 7)
GROUP BY DAY(`createdDate`)
ORDER BY `createdDate` DESC

error:
SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'table.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

Editoval kleinpetr (8. 1. 2016 7:56)

kleinpetr
Člen | 480
+
-5
-

Vyřešeno:

$db->query('SET sql_mode = ""');
PetrHH
Člen | 49
+
+5
-

Vyřešeno:

$db->query('SET sql_mode = ""');

Toto neni dobry napad. Tim odstranite i dalsi nastaveni. U mne na MySQL 5.7 vrati select @@sql_mode toto:

ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

Lepsi je pouzit:

$db->query("SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''))");
kleinpetr
Člen | 480
+
0
-

Dnes jsem opět narazil na stejný problém a v podstatě na něj narazím vždy když použiji GROUP_BY

tabulka

messages
idMessage	idUserRecipient		idUserSender
1			4					2
2			4					2

sql dotaz

SELECT *
FROM `messages`
WHERE (`idUserRecipient` = 4)
GROUP BY `idUserRecipient`
ORDER BY `idMessage DESC`

V čem je problém ? Díky

Editoval kleinpetr (12. 5. 2016 13:38)

CZechBoY
Člen | 3608
+
0
-

Tak groupujes podle jednoho sloupce a vybiras jich vic. Navic nevidim agregační funkci tak ten group by postrada smysl.. distinct?
Nebo jakej ocekavas vysledek?

Editoval CZechBoY (12. 5. 2016 13:10)

kleinpetr
Člen | 480
+
0
-

vysledek má být vždy poslední zpráva pouze k jednomu recipientovi

kleinpetr
Člen | 480
+
0
-

Zkouším s DISTINCT

SELECT DISTINCT `idUserRecipient`, `idMessage`, `idUserSender`
FROM `messages`
WHERE (`idUserRecipient` = 4)
ORDER BY `idMessage` DESC

ale stále vrací dva záznamy.

Editoval kleinpetr (12. 5. 2016 13:38)

CZechBoY
Člen | 3608
+
0
-

Pokud potrebujes jen jeden radek tak pridej LIMIT 1, pokud budes chtit pro kazdou skupinu posledni tak viz google :)
soory, nefunguje mi schranka na founu :(

Editoval CZechBoY (12. 5. 2016 14:23)

kleinpetr
Člen | 480
+
0
-

nene právě, že nepotřebuji jen jeden řádek, ale vždy od každé skupiny jeden řádek.

messages
idMessage   idUserRecipient     idUserSender
1           4                   2
2           4                   2
3			5					3
4			5					2

a z tohohle potřebuji vrátit k recipientovi 4 zprávu 2 a k recipientovi 5 zprávu 4

Unlink
Člen | 298
+
+1
-

Ako píše @CZechBoY, toto nesúvisí s nette, takže lepšie bude použiť google, napr. https://stackoverflow.com/…n-each-group

kleinpetr
Člen | 480
+
0
-

Díky :) asi jsem to měl spíš vložit do obecné diskuse. Každopádně díky za rady

PetrHH
Člen | 49
+
-1
-

Mozna pomuze:

SELECT `idUserRecipient`, max(idMessage), `idUserSender`
FROM `messages`
WHERE (`idUserRecipient` = 4)

Editoval PetrHH (12. 5. 2016 17:11)

kleinpetr
Člen | 480
+
0
-

Díky, ale to asi úplně neřeší můj problém.. Zkoušel jsem něco takového

SELECT (
	SELECT *
	FROM `message_user`
	WHERE `idUserSender` = `user.id` OR `idUserRecipient` = `user.id`
	ORDER BY `idMessage` DESC
	LIMIT 1) AS `message`
FROM `user`
LEFT JOIN `message_user` ON `user`.`id` = `message_user`.`idUserRecipient`
WHERE (`message_user`.`idUserRecipient` = 5 OR `message_user`.`idUserSender` = 5)

Výsledek má být např. takovýhle

Petr Klein
uživatel: Ahoj jak se máš ? v 10:00

Jan Novák
vy: máš zítra čas ? v 15:20

Tudíž vytáhnout poslední zprávu ze spojení uživatel:uživatel

Editoval kleinpetr (13. 5. 2016 12:36)

PetrHH
Člen | 49
+
0
-

Mohlo by fungovat neco takoveho. Bohuzel nemam struktury. nemohu poradit vice

select max(message_user.idMessage),message_user.msg from user
LEFT JOIN
  message_user ON user.id = message_user.idUserRecipient
WHERE
  (message_user.idUserRecipient = 5 OR message_user.idUserSender = 5)

ve sloupsi message_user.msg je ulozena vlastni zprava, kterou potrebujete zobrazit.

kleinpetr
Člen | 480
+
0
-

Díky za snahu, dnes odzkouším a dám vědět, zatím jsem to odložil, protože mě jinak opravdu nic nenapadá.. Takže zkusím a uvidím, kdyžtak dám větší info o struktuře. Každopádně mockrát díky :)

kleinpetr
Člen | 480
+
0
-

Takže, sice to nějaké záznamy vrací, ale chová se to zvláštně..

tabulky


user
id, name, ...

message
id, date, message

message_user
idMessage, idUserRecipient, idUserSender, read

To je vše.. Co potřebuji:
Já jsem přihlášený uživatel a mám ID 5 nyní když půjdu do inboxu, tak bych chtěl zobrazit seznam zpráv viz. gmail, vpodstatě jste v doručených, ale vidíte i svou poslední odpověď. Co se týče vláken, tak to neřeším, mám zkrátka jedno vlákno tudíž stejně jako v telefonu messages.

Nyní tedy zjednodušeně potřebuji vytáhnout veškeré uživatele se kterými jsem já, jakožto ID 5, ve vztahu buďto jako příjemce nebo jako odesílatel a vypsat jejich seznam, to by šlo, jenže ke každému tomu uživateli potřebuji vytáhnout poslední známou vazbu z tabulky message_user, tudíž nejvyšší IdMessage a zároveň ty uživatele podle idMessage poslední zprávy seřadit.

Můj pokus dle vašeho návrhu byl takovýhle

SELECT
  max(`message_user`.`idMessage`)
 `message`.`message` AS `lastMessageContent`,
 `message`.`date` AS `lastMessageDate`,
 `message_user`.`read` AS `lastMessageRead`,
 `user`.`id`,
 `user`.`name`
FROM `user`
LEFT JOIN `message_user` ON `user`.`id` = `message_user`.`idUserRecipient`
LEFT JOIN `message` ON `message_user`.`idMessage` = `message`.`id`
WHERE (`message_user`.`idUserRecipient` = 5 OR `message_user`.`idUserSender` = 5)

ale to vypisovalo první zprávu ve vlákně, když jsem max odstranil, tak to zobrazuje pouze poslední odeslanou zprávu a je to takové zamícháné, že místo jména uživatele je mé jméno apod..

Díky za nápady a hlavně za váš čas.

Editoval kleinpetr (18. 5. 2016 0:44)

Unlink
Člen | 298
+
0
-

Nejako takto

SELECT
    u1.id, u1.name,
    u2.id AS lastMessageSenderId, u2.name AS lastMessageSenderName,
    mu1.read,
    message.`date`, message.message
    FROM message_user mu1
    LEFT JOIN message_user mu2 ON (
        (
             (mu2.idUserRecipient = mu1.idUserRecipient AND mu2.idUserSender = mu1.idUserSender)
             OR (mu2.idUserRecipient = mu1.idUserSender AND mu2.idUserSender = mu1.idUserRecipient)
        ) AND mu1.idMessage < mu2.idMessage
    )
    LEFT JOIN message ON (message.id = mu1.idMessage)
    LEFT JOIN user u1 ON (u1.id = IF(mu1.idUserRecipient = 1, mu1.idUserSender, mu1.idUserRecipient))
    LEFT JOIN user u2 ON (u2.id = mu1.idUserSender)
WHERE (mu1.idUserRecipient = 1 OR mu1.idUserSender = 1) AND mu2.idMessage IS NULL
ORDER BY mu1.idMessage DESC

S tým, že ak nepotrebuješ aj meno odosielateľa poslednej správy, tak ten druhý join na user tabuľku môžeš vyhodiť.

Každopádne, pri pár záznamoch to nemá problém, ale ak budeš mať tých záznamov veľa, tak sa bojím že to výkonovo padne.
Hlavným problémom je tá časť:

(mu2.idUserRecipient = mu1.idUserRecipient AND mu2.idUserSender = mu1.idUserSender)
 OR (mu2.idUserRecipient = mu1.idUserSender AND mu2.idUserSender = mu1.idUserRecipient)

Ktorá znemožní využitie akého koľvek indexu.

Zlepšiť by sa to dalo využitím nejakých „threadov“, pretože ak by sa to nejoinovalo pomocou tohto výrazu, ale pomocou nejakého threadId tak by sa využili indexy a nebolo by to až tak zlé.

Každopádne, namiesto tohto by som skôr odporúčal vytvoriť nejakú pomocnú tabuľku, ktorá pre každého používateľa bude udržovať zoznam posledných správ…

EDIT:
Dalo by sa tomu pomôcť ale tiež to nieje nejaké extra výhra

SELECT
    u1.id, u1.name,
    u2.id AS lastMessageSenderId, u2.name AS lastMessageSenderName,
    mu1.read,
    message.`date`, message.message
    FROM message_user mu1
    LEFT JOIN message_user mu2 ON (
        (
             (mu2.idUserRecipient = mu1.idUserRecipient AND mu2.idUserSender = mu1.idUserSender)
        ) AND mu1.idMessage < mu2.idMessage
    )
    LEFT JOIN message_user mu3 ON (
        (
             (mu3.idUserRecipient = mu1.idUserSender AND mu3.idUserSender = mu1.idUserRecipient)
        ) AND mu1.idMessage < mu3.idMessage
    )
    LEFT JOIN message ON (message.id = mu1.idMessage)
    LEFT JOIN user u1 ON (u1.id = IF(mu1.idUserRecipient = 1, mu1.idUserSender, mu1.idUserRecipient))
    LEFT JOIN user u2 ON (u2.id = mu1.idUserSender)
WHERE (mu1.idUserRecipient = 1 OR mu1.idUserSender = 1) AND mu2.idMessage IS NULL AND mu3.idMessage IS NULL
ORDER BY mu1.idMessage DESC

Editoval Unlink (18. 5. 2016 8:54)

kleinpetr
Člen | 480
+
0
-

Díky moc za vyčerpávající příklad, vyzkouším to a uvidím jak a to bude chovat, popřípadě to vypustím a zkusím vymyslet jiné řešení zpráv, eventuelně zkusím vymyslet pomocnou tabulku :)

Díky moc všem :)

Editoval kleinpetr (18. 5. 2016 13:08)

kleinpetr
Člen | 480
+
0
-

Takže výsledek je takový:

do tabulky message_user jsem přidal sloupec ID a vytvořil jsem pomocnou tabulku

message_user_last
idMessageUser	idUser1		idUser2

pokud uzivatel napise nekomu zpravu napr. 4 ⇒ 5, tak vyhledam vztah

WHERE idUser1 = 4 AND idUser2 = 5 OR idUser1 = 5 AND idUser2 = 4

To mi vrátí záznam s id na poslední zprávu, aktuální zprávu uložím a updatnu záznam o nové id.
Pokud neexistuje tak ho vytvořím, ve finále tu tabulku procházím a dotahávám si z ní potřebné data přes cizí klíče + podmínkou zjišťuji zda jsem jako přihlášený user v té poslední zprávě jako sender nebo recipient a podle toho si vytáhnu data z usera, které potřebuji.

Takže finální dotaz vypadá nějak takhle

SELECT
		IF(`messageUser`.`idUserRecipient` = 4,`idUserSender`.`name`,`idUserRecipient`.`name`) AS `otherUserName`,
		IF(`messageUser`.`idUserRecipient` = 4,`messageUser`.`idUserSender`,`messageUser`.`idUserRecipient`) AS `otherUserId`,
		IF(`messageUser`.`idUserSender` = 4, 1, 0) AS `iAmSender`,
 		`messageUser`.*,
	 	`message`.*
	FROM `message_user_last`
		LEFT JOIN `message_user` `messageUser` ON `message_user_last`.`idMessageUser` = 	`messageUser`.`id`
		LEFT JOIN `message` ON `messageUser`.`idMessage` = `message`.`id`
		LEFT JOIN `user` `idUserSender` ON `messageUser`.`idUserSender` = `idUserSender`.`id`
		LEFT JOIN `user` `idUserRecipient` ON `messageUser`.`idUserRecipient` = `idUserRecipient`.`id`
	WHERE (`idUser1` = 4 OR `idUser2` = 4)
	ORDER BY `message`.`date` DESC

Nepodařilo se mi nějak sloučit podmínky v SELECTU kdyby někdo věděl, tak určitě přivítám :)

Díky moc za pomoc a možná se to bude někomu hodit :)

Editoval kleinpetr (19. 5. 2016 0:30)