![]() |
Avancerad SQL-fråga
Hej, detta kanske inte är så avancerat för vissa men för mig är det så. Vill också bli bättre på SQL.
Jag har 3 tabeller. Dom heter users, items och member_items. Såhär ser allting ut (.png):http://i.imgur.com/kZ7V3hN.png Man kan äga flera vapen av samma typ också. Rader i member_items kan då se ut såhär: :: Där ser ni att user_id kan äga 3 vapen av samma typ. HTML-kod:
user_id item_id Detta är mitt försök, längre kan jag inte komma. HTML-kod:
SELECT user.id, SUM( user.attack + user.defence) AS total_power |
Så här kanske
Kod:
SELECT users.id, SUM(users.attack + users.defence + items.extra_attack + items.extra_defence) AS total_power |
Hmm, nu kommer bara de users som har vapen med. Går det att göra om frågan så även dom utan vapen syns i tabellen?
Lyckades lösa det genom några modifieringar, tack för grundstrukturen :) PHP-kod:
|
En följdfråga...
Vill hämta en persons rang(högst total_power = rang #1). Detta funkar men går det att göra på ett snyggare sätt? Vill undvika att ha en statisk kolumn i usertable som håller reda på rangen. PHP-kod:
|
Du har ingen PK i member_items och det gör att när du har fler items av samma typ så finns ingen möjlighet att identifiera enskilda tupler (förekomster) och hantera dem individuellt, annat än med rownumber (databasberoende). Du borde lägga till en egen PK i member_items så du kan identifiera enskilda tupler där.
Det verkar inte finnas någon RI (referencial Integrity) definierad på de Främmande nycklarna från member_items till users respektive items. När du ställer en SQL-fråga här på forumet vore kanonbra om du lägger upp DDL-script för att skapa tabellerna och kanske lite insert-satser med exempeldata. Då det går snabbt för mig att lägga upp en exempeldatabas, och det går då snabbare att hjälpa dig, och du får troligen fler svar. Jag tycker det är kul att lösa SQL-problem så jag hjälper gärna till om jag kan och har tid. |
Okej, vad betyder tupler? Varför är det i detta fall nödvändigt att skilja alla member_items åt med PK?
Referencial Integrity har jag inte greppat, vill du förklara det för mig och vad som gäller i detta fall? Och med DDL menar du typ att ja ska skriva upp alla tabellstrukturer? Har hört att man helst ska använda SQL-fiddle? Kommer med alla sannolikhet att fråga mer om SQL så ja vill gärna veta vad det är för något :) |
Tupler (plural) -> Är den matematiska benämningen på rader i en tabell.
Grader (plural) -> Den matematiska benämningen på kolumner i en tabell. Referncial Integrity -> Ett sätt att låta databasen kontrollera att du bara använder giltiga Främmande nycklar (dvs värden som är Primärnyckel i en annan tabell, samt en del andra små trevliga saker som har med uppdatering och borttagning av primärnycklar som samtidigt är främmande nycklar i en annan tabell). DDL-Script -> De SQL-satser du skriver som innehåller CREATE TABLE... dvs där du definierar strukturen. Detta kallas för Data Definition Language och är en delmängd av det gigantiska SQL-språket. Varför behövs en primärnyckel i member_items-tabellen? En viktig anledning till att använda primärnycklar i alla tabeller är att det är en del av den grundläggande databasteorin, och enligt andra och tredje normalformen måste varje tuple (dvs rad i tabellen) kunna identifieras på ett unikt sätt. ett vanligt sätt är att använda ett löpnummer 1,2,3,4... men man kan använda andra datatyper. Du har använd PK (primärnyckel) i tabellerna users och item för att kunna identifiera varje enskild tuple. Om du ska radera en enskild tuple i member_items-tabellen så går inte det därför att det inte går att identifiera en enstaka tuple, det bryter helt enkelt mot de mest elementära normaliseringsreglerna. När du tar bort ett värde idag med en enkel delete-sats så kommer alla värden med samma värden att raderas. |
Okej, ännu en del saker som är oklara men det fastnar väl inom kort...
Själv stötte jag på ett problem här som jag aldrig tidigare behövt göra. Jag vill radera alla rader i tabellen nedanför FÖRUTOM de 30 senaste raderna som baseras på message_time. Det är väl samma sak med message_id tror jag. Mitt försök där jag fick stop. Här raderar jag alla förutom den senaste. PHP-kod:
PHP-kod:
|
Du borde skriva något i stil med det här:
Kod:
-- Kod för MySQL Kod:
-- Kod för MS SQL-Server När du använder max i din subquery så får du bara en tuple, men du ville ha 30. |
Efter att jag testkörde frågan såg jag att det var lite trubbel i MySQL, som inte uppträder i MSSQL, så här kommer en uppdaterad SQL-kod som jag har testkört utan data:
Kod:
DELETE FROM `shoutbox` |
Efter lite intrimning så kom jag fram till den här SQL-satsen som blir lite snabbare särskilt om det är många rader man vill behålla:
Kod:
DELETE FROM `shoutbox` |
Gör om det hela till en Stored Procedure så blir det enklare att anropa från prograkoden och det blir enklare att göra ändringar:
Kod:
USE `secag`; Kod:
CALL `secag`.`DeleteAllMessagesButLast30` (); |
Conny, efter testast MySQL koden så fick jag detta:
PHP-kod:
|
Citat:
|
Såg inte att det fanns en sida 2 när jag postade :-)
Det funkar nu i alla fall. Men PROCEDURE är typ som en funktion fast i SQL? Hur tillämpar jag detta då? Jag kör liksom bara en enkel $db->query("SQL"); i mitt PHP-skript. Ska jag lägga in den där proceduren i SQL innan? |
Du måste först lägga in proceduren i MySQL, det gör du från administrationsverktyget och kör detta som en SQL-sats (enligt första Code-rutan i inlägg #12 i denna tråd). När det väl har körts kan du testa att den funkar enligt andra Code-rutan i inlägg #12 i denna tråd.
Så här skriver du i PHP för att anropa en SP: Kod:
if (!$db->query("CALL DeleteAllMessagesButLast30()")) |
Stor cred till Conny för hans hjälpsamhet som i många andra SQL-trådar.
Jag vill bara passa på att säga att stored procedures har både många fördelar och nackdelar. För det mesta används de inte för webb-applikationer med MySQL (förutom möjligtvis inom ASP.NET-världen där de nog inte är ovanliga trots MySQL). Men det finns 100 anledningar till att använda dom och 100 anledningar till att inte använda dom beroende av situation, arkitektur, applikation, organisation och inte minst databasmjukvaran. |
Eftersom MySQL stöder SP numera så finns möjligheten att använda SP och då höjer du prestanda och förbättrar enkelheten i dina PHP-applikationer samt du kan höja säkerheten.
Man har dessutom möjligheten till att samla all affärslogik på ett enda ställe, i databasen. Fördelarna är många, nackdelarna väldigt få. |
Citat:
SP anses generellt vara snabbare för att frågan bara behöver parsas och optimeras en gång. Med MySQL så har varje session (tråd) en egen cache som måste byggas. Det betyder dels att nyttan blir väldigt liten och att det vid hög concurrency och många SPs riskerar att få totalt motsatt effekt då minnesanvändning per tråd riskerar att dra iväg. Detta förutsatt att du inte har någon connection pooling på applikationsnivå, vilket är sant för över 99% av webbapplikationer med PHP. Så ur prestandasynpunkt finns det för MySQL ingen poäng att använda SPs förutom om man har en effektiv connection pooling för sin applikation. Angående huruvida det rent arkitekturmässigt är en bra approach finns det många aspekter att väga in, t ex: - Versionshantering och continous integration - blir mycket mer avancerade och omständiga om man väljer att lägga sin affärslogik i SPs. - Du blir beroende av en databas. Vill du stödja flera olika databassystem senare med samma mjukvara så är det bara att börja om från början med ALL affärslogik. - Vill man ha all affärslogik i databasen blir det allt som oftast en konflikt vid hantering av större mängder binär data. I databasen trots att det är ett ineffektivt val eller separera den biten av affärslogiken och därmed inte ha allt i databaslagret. - Ska flera avdelningar/företag/applikationer osv gå direkt mot samma databas KAN det vara en fördel att använda SPs men det gör inte sällan att det skapas förutsättningar om hur andra ska använda datan. - SPs är lätta att testa isolerat, men gör integrationstester desto svårare. - Användande av SPs lägger ofta onödigt mycket last i databaslagret vilket är den svåraste delen att skala och optimera. - Använder du ett smidigt applikationsspråk så är utvecklingstiden och tiden som går åt till underhåll desto högre i SPs (inkluderar ASP.NET, PHP, RoR etc). Inte på något sätt uttömmande. Det finns mängder av IFs och BUTs som gör det lämpligare eller olämpligare. Men att blanda affärslogik i applikationen och i SPs skulle jag vilja påstå är något man helt och hållet ska hålla sig borta ifrån. Vilket nog precis blir fallet för TS. |
Intressant, har tidigare funderat på varför SP's anses mer av "best practice" i asp.net världen medans det i php/mysql inte är så diskuterat eller använt.
Hur brukar ni hantera stora SQL frågor, lägger ni dem i ett "datalager" ? Det känns bökigt när man har sql frågor med typ 10 joins att lägga in dem i en vanlig php sträng, jag har tidigare övervägt att lägga in dessa i SP's istället för att få enklare underhåll. |
Citat:
Själv föredrar jag generellt att hålla mig borta från SPs av främst 2 anledningar. - Lättare byggprocess och inget extra att tänka på vid förändringar i affärslogik utan förändringar i databasstruktur (vilket iallafall under mina 15 år med webb varit mycket mycket vanligare). - Snabbare utvecklings i ett välutvecklat scriptspråk vilket leder till fler personer med kompetens. Och alternativen är inte SPs eller en sträng i ett scriptspråk. Query builders, content repositories, ORMs osv finns bibliotek för i alla språk. Vidare vet jag inte huruvida det är så etablerat som best practice generellt i .NET. Använder du entity framework t ex är det väl snarare tvärtom (går att använda men är manuellt extrapill, tror jag?). Och det är väl ändå välanvänt? |
Citat:
Men givet att man vill använda ren sql är det väl endast Sp eller sträng sql som gäller,eller Vyer. Citat:
Men har man SQL direkt i c# koden brukar en del tycka det är rätt illa. Säker delvis p.g.a att det krävs omkompilering av Visual Studio projektet för att ändra sql:en. |
Har ett nytt problem nu.
Koden för att få "total_power" ifrån enskilda spelare fungerar bra. Men nu har jag en tabell med en lista på spelare där jag måste räkna ihop allas "total_power" till en enda stor summa. Vi kan kalla den "MEGA_TOTAL_POWER". Jag tror att man någonstans skall lägga en s.k. sub-query? Här är mitt försök vilket ger mig ett felmeddelanden som jag inte kan förstå: PHP-kod:
PHP-kod:
PHP-kod:
http://www.sqlteam.com/article/aggre...ed-sub-queries |
Jag skulle nog försöka dela upp det i fler queries och undvika sub queries.
Den länken gäller sql server så det kan vara felaktig info även om sql standarden kan vara korrekt. |
Flera querys har väl en tendens att bli jobbiga? Alltså mer krävande?
|
Citat:
Men det kan faktiskt gå snabbare om du istället för en avancerad sql kör två, tre enskilda queries och kanske gör vissa summeringar i php istället, vilket avlastar databasen. Det kan även vara lättare att felsöka och optimera de enskilda frågorna mha EXPLAIN Select. Nu har jag svårt att greppa just detta case, men om det går att dela upp det så tycker jag du ska testa det.. |
Okej men jag kan förklara hur det ska vara.
En tabell heter crews, den hämtar jag all information ifrån "FROM crews" En tabell som heter crew_members där det kan finnas upp till 80 rader. Här gör jag "JOIN crew_members ON crews.id = crew_members.crew_id" Och crew_members innehåller endast user_id & crew_id. Ifrån detta user id så vill jag hämta en hel del information(åtminstone username och sen total_power). Vill inte loopa en query 80 gånger, känns som det blir mycket segare då. |
Kan du bidra med koden för att få fram total_power som väl fungerar.
Kan du bidra med fler INSERT-satser så man kan få lite testdata att jobba med? |
Har inte helt koll på vad du vill göra och eller hur dina tabeller ser ut, men om jag inte är helt fel ute så borde du kunna göra något liknande detta:
SELECT m.username (eller liknande för att plocka ut de kolumner du vill ha) FROM crew c JOIN crew_members cm on cm.crew_id = c.crew_id JOIN members m on m.user_id = cm.user_id WHERE c.crew_id = 1 (eller vilket id du vill hämta ut medlemmarna ifrån) Vill du även ta ut t.ex. total_power så får du joina på de tabeller som innehåller informationen om detta (items? om jag minns rätt från tidigare poster?) samt gruppera på m.user_id och göra en summering på alla items för varje medlem. |
Löste det på egen hand..
PHP-kod:
Med denna query: PHP-kod:
PHP-kod:
Hoppas att någon hänger med i min tankegång eller har en bättre lösning |
Hänger inte riktigt med på vilket resultat du vill ha ut i din senaste fråga. Alltså vilka kolumner och data vill du få fram?
Hur ska du använda resultatet? Ska du visa en lista på medlemmar med total power, samt alla items de har? Om det inte är för många medlemmar som ska visas per sida så är det nog enklare/bättre att lösa detta i php-koden med flera querys. Antingen en med alla medlemmar och deras total power och sedan en per medlem för att hämta upp deras items till den medlemmen eller en för alla medlemmarna och en för alla items för dessa medlemmar och sedan php-logik för att sätta ihop resultaten. Alternativt kan du titta på group_concat, om du bara behöver en textsträng med items dvs. Då kan också så klart joina ihop member data med total power och items med en subquery för att få ut allt i samma fråga. |
Så här skulle du konna lägga in get_crew_power() som en SP:
Kod:
DELIMITER $$ Kod:
if (!$db->query("CALL get_crew_power(" . crew_id . ")")) För att anropa SPn från PHP UTAN felhantering skriver du så här: Kod:
$db->query("CALL get_crew_power(" . crew_id . ")") |
Alla tider är GMT +2. Klockan är nu 14:04. |
Programvara från: vBulletin® Version 3.8.2
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Svensk översättning av: Anders Pettersson