WN

WN (https://www.wn.se/forum/index.php)
-   Serversidans teknologier (https://www.wn.se/forum/forumdisplay.php?f=4)
-   -   UPDATE och SELECT i samma query (MySQL) (https://www.wn.se/forum/showthread.php?t=1045827)

Linuus 2011-01-11 18:03

UPDATE och SELECT i samma query (MySQL)
 
Tjena

Jag har en två tabeller, produkter och varumärken. I produkt-tabellen finns ett id som pekar på ett varumärke.

Nu skulle jag vilja köra en query direkt i PHPMyAdmin som räknar hur många produkter varje varumärke har och sparar detta i fältet "prod_count" i varumärkestabellen.

Kan jag göra en SELECT och UPDATE i samma query?
Hur ser en sådan query ut i så fall?

Själva SELECT-delen är ju ganska enkel, den blir såhär:
SELECT brand_id, count(brand_id) AS prod_count FROM `products` GROUP BY `brand_id`

Därefter vill jag ju då uppdatera 'brand.prod_count' med dessa värden.

nosnaj 2011-01-11 18:36

Varför ska du spara värdet i tabellen när du kan räkna fram det?

Men visst borde det gå, testat i stil med:

update brand set prod_count = (select count(*) from products where brand.brand_id = products.brand_id group_by brand_id)

KarlRoos 2011-01-11 18:36

Finns det någon anledning till att du vill göra dem i samma query?

Annars gör du det bara med PHP som följande:

PHP-kod:

$result mysql_query('SELECT brand_id, count(brand_id) AS prod_count FROM `products` GROUP BY `brand_id`');

while(
$item mysql_fetch_assoc($result)){
mysql_query('UPDATE `brand` SET `prod_count`=' $item['prod_count'] . ' WHERE `brand_id`=' $item['brand_id'];



Linuus 2011-01-11 19:03

Tack för svaren

nosnaj: ska testa den.

Anledningen till att jag vill göra allt i ett svep i MySQL är för att jag bara ska köra queryn 1 gång. Jag har X antal tusen produkter och ett par hundra varumärken. Tidigare använde jag bara en VIEW där jag hade ett fält, prod_count, som räknades fram. Problemet är nu att jag vill visa alla varumärken samtidigt i en lista där det även står hur många produkter de har. Då måste databasen räkna hur många produkter varje varumärke har varje gång. Detta tar tid och funkar alltså inte.

Därför vill jag göra en uträkning nu och sen kommer detta värde justeras automatiskt när jag lägger till/tar bort produkter.

nosnaj 2011-01-11 19:06

Antar du har satt index osv på dina tabeller?
Att databsen börjar bli slö vid endast tusentals rader är inget bra tecken...

Men testa de två alternativ du har fått, ibland får man gå ifrån all fin normalisering :)

Linuus 2011-01-11 20:05

Citat:

Ursprungligen postat av nosnaj (Inlägg 20387275)
Antar du har satt index osv på dina tabeller?
Att databsen börjar bli slö vid endast tusentals rader är inget bra tecken...

Men testa de två alternativ du har fått, ibland får man gå ifrån all fin normalisering :)

Fast tänk dig att du har 1000 varumärken och 10.000 produkter. Sen ska du lista alla varumärken såhär:
Varumärke (xx produkter)

Då måste den VIEW du skapat gå igenom 10.000 rader för varje varumärke för att räkna de produkter den är relaterad till, vilket ger att den måste kolla i 1000*10.000=10 000 000 rader.

Eller har jag fel?

Inte konstigt att det tar en stund annars :)

(Sen kan man ju gå vidare med cache osv...)

Conny Westh 2011-01-11 20:06

Linuus; jag säger som nosnaj, har du problem med prestanda på en sån fråga du ställer någon enstaka gång så har du troligen major problem med index.

Detta är et typexempel på en fråga som man aldrig spara resultatet av utan att man tar fram den vid behov.

Så här skulle jag kunnat gjort för att lösa uppgiften (Alla kolumner i WHERE satsen ska givetvis vara indexerade på rätt sätt):

Kod:

SELECT
    Brand_id,
    Brand,
    (
        select IFNULL(count(*),0)
        FROM products
        where brand.brand_id = products.brand_id
    ) AS Antal
FROM Brands


Då visas alla brand du har och antalet förekomster/Tupler oavsett om det är null eller inte.

Det är rent BS att subselectar skulle vara långsamma! Jag har använt 8 subselect i en dynamisk SQL-fråga mot en tabell med joinar från flera andra tabeller med drygt 200 000 personuppgifter (ett 40 tal kolumner med massor av persondata) och den var blixtsnabb.

Jag använde MS-SQL server 6.0 då

Linuus 2011-01-11 20:15

Tack för svaren

Såhär blev min query till slut

Kod:

UPDATE `brands` b
SET b.prod_count = (
        SELECT count(brand_id)
        FROM `products` AS p
        WHERE b.brand_id = p.brand_id
);

Och det verkar ha blivit rätt :)

Linuus 2011-01-11 20:23

Tar tillbaka det jag sa...hade visst fel!
Mixtrade lite med mina INDEX och nu verkar faktiskt min VIEW gå att använda utan längre väntetider.

Tack för det :)

nosnaj 2011-01-11 20:44

Citat:

Ursprungligen postat av Linuus (Inlägg 20387291)
Tar tillbaka det jag sa...hade visst fel!
Mixtrade lite med mina INDEX och nu verkar faktiskt min VIEW gå att använda utan längre väntetider.

Tack för det :)

Vad gör din view?

Nyfiken vad folk använder dessa till då jag i princip aldrig använder mig utav dom.

Clarence 2011-01-11 20:53

Citat:

Ursprungligen postat av Linuus (Inlägg 20387291)
Tar tillbaka det jag sa...hade visst fel!
Mixtrade lite med mina INDEX och nu verkar faktiskt min VIEW gå att använda utan längre väntetider.

Tack för det :)

Vid ett par tusen bör det inte ge några problem, men det är inte effektivt. Din första lösning med denormalisering är desto effektivare. Nosnajs query är enklaste och effektivaste sättet att uppdatera den. Men om man har väldigt många rader är det prestandamässigt bättre att dela upp den i X queries med ranges (ej en för varje brand) och köra dem med liten tidsintervall emellan.

Att spara denormaliserad data gör inte att man inte sparar och får nästan all nytta av normaliserade datan. Den stora nackdelen du får är att uppdatera den denormaliserade datan manuellt vilket ger en liten liten overhead vid skrivning. Att inte spara denormaliserad data ger däremot i många fall, liksom ditt, en rejäl overhead vid läsning. Skapar man en vy får man allt för ofta ännu mer overhead vid varje skrivning om den cachas då hela vyn invalideras med skrivning till någon tabell den beror på. Alternativt cachas inget och du får rejält med overhead vid varje läsning då du får läsa mer data, oavsett hur väl din indexering är genomgången.

I ditt fall går säkerligen din lösning bra - men jag ville ändå inflika med detta då jag tyckte din första lösning var rätt väg att gå.

Conny Westh 2011-01-11 21:24

Citat:

Ursprungligen postat av nosnaj (Inlägg 20387296)
Nyfiken vad folk använder dessa till då jag i princip aldrig använder mig utav dom.

Isolera/samla kunskap om systemet eller verksamheten
Vyer och för den delen SPn är mycket praktiska att använda om man vill ändra innehållet utan att ändra gränssnittet mot sin applikation.

Har man en vy som heter "topsales_view" så kan man skriva:

Kod:

SELECT productid, productname, salescount, salesamount, performancepoints, area, salesperson
FROM topsales_view

Men man kan när som helst förfina hur detta räknas ut och hur det sorteras redan i vyn. Detta utan att någon applikation som använder vyn behöver uppdateras.

Helt enkelt göra det enklare för utvecklaren!

Dölja komplexitet=mindre buggar
Ett annat användningsområde är om man har komplexa frågor som man anropar på flera ställen i sina applikationer, eller från flera applikationer så kan man samla komplexiteten i vyn och dölja den för applikationsutvecklaren, vilket minskar risken för buggar.

Prestanda
Prestandamässigt är både vyer och SPn generellt snabbare än dynamisk SQL eftersom frågorna förkompileras när man skapar sina vyer, SPn och de optimeras därmed av SQL-compilatorn.

Behörigheter
Man kan även styra behörigheter genom att endast tillåta att vissa applikationer enbart får komma åt att läsa (SELECT) på vissa vyer eller SPn och spärra direktåtkomst till övriga fysiska tabeller.

Enklare felsökning
Eftersom alla verksamhetsregler kan samlas på ett ställe som fysiskt ligger nära datat, så blir det enklare att hitta fel och åtgärda dem utan att kompilera om och distribuera ut updaterad applikationsprogramvara.

Renare och tydligare gränssnitt gentemot applikationerna

jonny 2011-01-12 06:57

Citat:

Ursprungligen postat av Linuus (Inlägg 20387287)
Tack för svaren

Såhär blev min query till slut

Kod:

UPDATE `brands` b
SET b.prod_count = (
        SELECT count(brand_id)
        FROM `products` AS p
        WHERE b.brand_id = p.brand_id
);

Och det verkar ha blivit rätt :)

Varför har du med ett AS i subqueryn men inte i queryn?

jonny 2011-01-12 06:59

Citat:

Ursprungligen postat av ConnyWesth (Inlägg 20387309)
Dölja komplexitet=mindre buggar

Eller så får du fler problem och jobbigare felsökning eftersom logiken finns även i datalagret.

Linuus 2011-01-12 08:53

Citat:

Ursprungligen postat av jonny (Inlägg 20387351)
Varför har du med ett AS i subqueryn men inte i queryn?

Det bara blev så... Jag var inte så noga då det bara är en query om ska köras EN gång.

Men vad är det för skillnad? Jag får exakt samma resultat om jag kör AS i queryn också.

jonny 2011-01-12 09:25

Citat:

Ursprungligen postat av Linuus (Inlägg 20387360)
Det bara blev så... Jag var inte så noga då det bara är en query om ska köras EN gång.

Men vad är det för skillnad? Jag får exakt samma resultat om jag kör AS i queryn också.

Det var just det som var min poäng - AS bör vara helt irrelevant och det går lika bra utan men du har med det på ett ställe och inte på det andra. Det är bra att vara enhetlig för att slippa skumma problem.

Linuus 2011-01-12 09:30

Citat:

Ursprungligen postat av jonny (Inlägg 20387364)
Det var just det som var min poäng - AS bör vara helt irrelevant och det går lika bra utan men du har med det på ett ställe och inte på det andra. Det är bra att vara enhetlig för att slippa skumma problem.

Absolut. Tack :)

Conny Westh 2011-01-12 09:48

Citat:

Ursprungligen postat av jonny (Inlägg 20387352)
Eller så får du fler problem och jobbigare felsökning eftersom logiken finns även i datalagret.

Tvärtom så får man enklare felsökning om man lägger affärsreglerna så nära datat som möjligt, då finns reglerna bara på ett ställe istället för att man ska lägga dem i varje klientapplikation man utvecklar.


Alla tider är GMT +2. Klockan är nu 19:24.

Programvara från: vBulletin® Version 3.8.2
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Svensk översättning av: Anders Pettersson