Kom ihåg mig?
Home Menu

Menu


mySQL - join eller group problem

 
Ämnesverktyg Visningsalternativ
Oläst 2011-12-23, 01:34 #1
aelanders avatar
aelander aelander är inte uppkopplad
Medlem
 
Reg.datum: Dec 2004
Inlägg: 146
aelander aelander är inte uppkopplad
Medlem
aelanders avatar
 
Reg.datum: Dec 2004
Inlägg: 146
Standard mySQL - join eller group problem

Jag har två tabeller

---------------------------------------
En produkttabell TabellA (i verkligheten avsevärt många fler kolumner)

Kod:

select * from TabellA
+----+-------+
| Id | Namn  |
+----+-------+
|  1 | Saab  |
|  2 | Volvo |
|  3 | Audi  |
|  4 | Lexus |
+----+-------+
och en TabellB med priser
Kod:

select * from TabellB
+----+-----+------+------------+
| Id | PId | Pris | Datum      |
+----+-----+------+------------+
|  1 |   1 |  500 | 2011-12-21 |
|  2 |   2 |  600 | 2011-12-21 |
|  3 |   3 | 1000 | 2011-12-07 |
|  4 |   3 | 1800 | 2011-12-13 |
|  5 |   3 | 1500 | 2011-12-18 |
|  6 |   4 |  320 | 0201-12-01 |
+----+-----+------+------------+

Priserna förändras över tid och jag vill spara all historik.
Här kan vi se att priset på produkt ändrats tre gånger i dec


Kod:

select PId,Namn,Pris,Datum from TabellA
left join TabellB on TabellB.PId = TabellA.Id
+------+-------+------+------------+
| PId  | Namn  | Pris | Datum      |
+------+-------+------+------------+
|    1 | Saab  |  500 | 2011-12-21 |
|    2 | Volvo |  600 | 2011-12-21 |
|    3 | Audi  | 1000 | 2011-12-07 |
|    3 | Audi  | 1800 | 2011-12-13 |
|    3 | Audi  | 1500 | 2011-12-18 |
|    4 | Lexus |  320 | 0201-12-01 |
+------+-------+------+------------+

Kod:

select PId,Namn,Pris,Datum from TabellA left
join TabellB on TabellB.PId = TabellA.Id
group by PId;
+------+-------+------+------------+
| PId  | Namn  | Pris | Datum      |
+------+-------+------+------------+
|    1 | Saab  |  500 | 2011-12-21 |
|    2 | Volvo |  600 | 2011-12-21 |
|    3 | Audi  | 1000 | 2011-12-07 |
|    4 | Lexus |  320 | 0201-12-01 |
+------+-------+------+------------+


Vad skriver jag för att det alltid ska välja senast pris? Alltså ett sånt här resultat.
Kod:

+------+-------+------+------------+
| PId  | Namn  | Pris | Datum      |
+------+-------+------+------------+
|    1 | Saab  |  500 | 2011-12-21 |
|    2 | Volvo |  600 | 2011-12-21 |
|    3 | Audi  | 1500 | 2011-12-18 | <-- detta
|    4 | Lexus |  320 | 0201-12-01 |
+------+-------+------+------------+
aelander är inte uppkopplad   Svara med citatSvara med citat
Oläst 2011-12-23, 02:04 #2
aelanders avatar
aelander aelander är inte uppkopplad
Medlem
 
Reg.datum: Dec 2004
Inlägg: 146
aelander aelander är inte uppkopplad
Medlem
aelanders avatar
 
Reg.datum: Dec 2004
Inlägg: 146
Får ju problem redan här:

Kod:

select * from TabellB;
+----+-----+------+------------+
| Id | PId | Pris | Datum      |
+----+-----+------+------------+
|  1 |   1 |  500 | 2011-12-21 |
|  2 |   2 |  600 | 2011-12-21 |
|  3 |   3 | 1000 | 2011-12-07 |
|  4 |   3 | 1800 | 2011-12-13 |
|  5 |   3 | 1500 | 2011-12-18 |
|  6 |   4 |  320 | 2011-12-01 |
+----+-----+------+------------+
Kod:

mysql> select * from TabellB group by PId;
+----+-----+------+------------+
| Id | PId | Pris | Datum      |
+----+-----+------+------------+
|  1 |   1 |  500 | 2011-12-21 |
|  2 |   2 |  600 | 2011-12-21 |
|  3 |   3 | 1000 | 2011-12-07 |
|  6 |   4 |  320 | 2011-12-01 |
+----+-----+------+------------+
Vill att den ska välja det senaste priset för en PId
aelander är inte uppkopplad   Svara med citatSvara med citat
Oläst 2011-12-23, 10:11 #3
Monkboll Monkboll är inte uppkopplad
Medlem
 
Reg.datum: Apr 2010
Inlägg: 157
Monkboll Monkboll är inte uppkopplad
Medlem
 
Reg.datum: Apr 2010
Inlägg: 157
Skulle jag varit dig hade jag haft en separat tabell för de nuvarande priserna. Varje gång du ändrar ett pris så körs en trigger som lägger till en post i en tabell som loggar priset.
Går säkert lösa på många sätt som sagt men så har jag gjort med andra typer av data som ska loggas.
Monkboll är inte uppkopplad   Svara med citatSvara med citat
Oläst 2011-12-23, 11:56 #4
Kekke Kekke är inte uppkopplad
Medlem
 
Reg.datum: Feb 2011
Inlägg: 198
Kekke Kekke är inte uppkopplad
Medlem
 
Reg.datum: Feb 2011
Inlägg: 198
Något sånt här borde fungera:

Kod:
SELECT TabellA.*, MAX(TabellB.datum), TabellB.pris from TabellA INNER JOIN TabellB on TabellB.PId = TabellA.id GROUP BY TabellA.id
Kekke är inte uppkopplad   Svara med citatSvara med citat
Oläst 2011-12-24, 11:59 #5
Conny Westh Conny Westh är inte uppkopplad
Klarade millennium-buggen
 
Reg.datum: Aug 2005
Inlägg: 5 166
Conny Westh Conny Westh är inte uppkopplad
Klarade millennium-buggen
 
Reg.datum: Aug 2005
Inlägg: 5 166
Här är ett enkelt sätt att göra det på, så du enbart får senaste priset:

Kod:
SELECT *
FROM ProduktPris pp1
WHERE pp1.PrisDatum = 
(
	SELECT DISTINCT Max(pp2.prisdatum) 
             FROM ProduktPris pp2
             WHERE pp2.Artikelnummer=pp1.Artikelnummer
)

Här är ett annat enkelt sätt att göra ungefär samma sak fast med produkttabellen med, så man set produkten i klartext:
Kod:
SELECT p.Artikelnummer, p.ProduktNamn, 
( 
    SELECT MAX(pp.Pris) 
    FROM ProduktPris pp 
    WHERE pp.Artikelnummer=p.Artikelnummer) AS Pris
FROM Produkt p

Senast redigerad av Conny Westh den 2011-12-24 klockan 12:14
Conny Westh är inte uppkopplad   Svara med citatSvara med citat
Oläst 2011-12-24, 12:23 #6
Conny Westh Conny Westh är inte uppkopplad
Klarade millennium-buggen
 
Reg.datum: Aug 2005
Inlägg: 5 166
Conny Westh Conny Westh är inte uppkopplad
Klarade millennium-buggen
 
Reg.datum: Aug 2005
Inlägg: 5 166
Dålig färändring i Forumet att man inte kan redigera egna inlägg mer än 15 minuter efter första spara!

Jag körde SQL-server men det borde vara närmast identisk SQL-kod.

Jag hade följande tabeller i mitt exempel:

Kod:
CREATE TABLE [dbo].[produkt]
(
	[Artikelnummer] [nvarchar](50) NOT NULL,
	[ProduktNamn] [nvarchar](50) NOT NULL
);


CREATE TABLE [dbo].[ProduktPris]
(
	[PrisID] [int] IDENTITY(1,1) NOT NULL,
	[Artikelnummer] [nvarchar](50) NOT NULL,
	[pris] [money] NOT NULL,
	[prisdatum] [datetime] NOT NULL
);

ALTER TABLE [dbo].[ProduktPris] 
ADD  CONSTRAINT [DF_ProduktPris_prisdatum]  
DEFAULT (getdate()) FOR [prisdatum];

Senast redigerad av Conny Westh den 2011-12-24 klockan 12:26
Conny Westh är inte uppkopplad   Svara med citatSvara med citat
Oläst 2011-12-24, 13:51 #7
pelmereds avatar
pelmered pelmered är inte uppkopplad
Har WN som tidsfördriv
 
Reg.datum: May 2010
Inlägg: 1 342
pelmered pelmered är inte uppkopplad
Har WN som tidsfördriv
pelmereds avatar
 
Reg.datum: May 2010
Inlägg: 1 342
Citat:
Ursprungligen postat av Monkboll Visa inlägg
Skulle jag varit dig hade jag haft en separat tabell för de nuvarande priserna. Varje gång du ändrar ett pris så körs en trigger som lägger till en post i en tabell som loggar priset.
Går säkert lösa på många sätt som sagt men så har jag gjort med andra typer av data som ska loggas.
Håller med om att det där är den bästa lösningen om det inte finns några andra hinder.
Du skulle ju rentav kunna ha priset och senaste uppdateringsdatum direkt i produkttabellen och sedan ha en tabell med prishistorik. Då slipper du joins helt och får en snyggare(enligt mig) och mer effektiv(bättre prestanda om du slipper joina) tabellstruktur.
pelmered är inte uppkopplad   Svara med citatSvara med citat
Oläst 2011-12-24, 17:06 #8
Conny Westh Conny Westh är inte uppkopplad
Klarade millennium-buggen
 
Reg.datum: Aug 2005
Inlägg: 5 166
Conny Westh Conny Westh är inte uppkopplad
Klarade millennium-buggen
 
Reg.datum: Aug 2005
Inlägg: 5 166
Citat:
Ursprungligen postat av ITisGood.se Visa inlägg
Håller med om att det där är den bästa lösningen om det inte finns några andra hinder.
Du skulle ju rentav kunna ha priset och senaste uppdateringsdatum direkt i produkttabellen och sedan ha en tabell med prishistorik. Då slipper du joins helt och får en snyggare(enligt mig) och mer effektiv(bättre prestanda om du slipper joina) tabellstruktur.
Håller inte med om att det blir effektivare, då det blir en hiskeligt komplicerat förfarande så fort det ska till en prisförändring. Det är effektivare att bara lägga in en extra post i en pristabell och sedan med en smart sql-fråga ta fram senaset priset.

Om man ska ha en historiktabell så måste man varje gång man gör én prisförändring först läsa fram det gamla priset för att lagra det i en historiktabell och sen lägga in det nya priset i "den aktuella pristabellen" detta måste även göras med en transaktion så det går att backa om någon del inte går igenom. Med den enklare varianten så slipper man hantera transaktioner.

Vad vi pratar om för prestandaeffektivisering är endast frågan om millisekunder vid prisfrågan, det tycker i vart fall inte jag är värt den betydligt mer komplicerade strukturen som det lätt kan bli fel i, det är mindre risk att det blir fel om man bara lägger till en extra insert varje gång priset förändras.

Den mer komplexa lösningen kräver även mer "knowledgein the head" dvs kunskap hos den som administrerar databasen än den enklare lösnuingen där det är fokus på "Knowledge in the world" (dvs mer kunskap finns i databasen i sig själv).

Senast redigerad av Conny Westh den 2011-12-24 klockan 17:19
Conny Westh är inte uppkopplad   Svara med citatSvara med citat
Oläst 2011-12-24, 18:05 #9
Conny Westh Conny Westh är inte uppkopplad
Klarade millennium-buggen
 
Reg.datum: Aug 2005
Inlägg: 5 166
Conny Westh Conny Westh är inte uppkopplad
Klarade millennium-buggen
 
Reg.datum: Aug 2005
Inlägg: 5 166
Gör man om det hela till StoredProcedures så blir det lite snabbare och enklare att hantera (man kan då ange valfritt datum när man vill veta aktuell prislista, vilket gör att man kan hantera framtida prisförändringar):

Kod:
CREATE PROCEDURE [dbo].[SenasteProduktPrisAlt1]
	-- Add the parameters for the stored procedure here
	@Datum datetime = GetDate
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

	--declare @datum datetime
	--set @datum = GETDATE()
	SELECT     PrisID, Artikelnummer, pris, prisdatum
	FROM         dbo.ProduktPris AS pp1
	WHERE     PrisDatum <= @Datum 
	AND 
	(
		prisdatum =
		(
			SELECT DISTINCT MAX(prisdatum) AS prisdatum
			FROM          dbo.ProduktPris AS pp2
			WHERE      (Artikelnummer = pp1.Artikelnummer)
		)
	)
END
GO

-- Tester
declare @datum datetime
set @datum = GETDATE()
exec [dbo].[SenasteProduktPrisAlt1] @datum;

exec [dbo].[SenasteProduktPrisAlt1] '2011-12-15 23:59:59';
exec [dbo].[SenasteProduktPrisAlt1] '2011-12-18';
exec [dbo].[SenasteProduktPrisAlt1] '2011-12-18 23:59:59';
exec [dbo].[SenasteProduktPrisAlt1] '2011-12-21 23:59:59';
exec [dbo].[SenasteProduktPrisAlt1] '2011-12-24 23:59:59';

Senast redigerad av Conny Westh den 2011-12-24 klockan 18:18
Conny Westh är inte uppkopplad   Svara med citatSvara med citat
Oläst 2011-12-24, 21:59 #10
pelmereds avatar
pelmered pelmered är inte uppkopplad
Har WN som tidsfördriv
 
Reg.datum: May 2010
Inlägg: 1 342
pelmered pelmered är inte uppkopplad
Har WN som tidsfördriv
pelmereds avatar
 
Reg.datum: May 2010
Inlägg: 1 342
Citat:
Ursprungligen postat av ConnyWesth Visa inlägg
Håller inte med om att det blir effektivare, då det blir en hiskeligt komplicerat förfarande så fort det ska till en prisförändring. Det är effektivare att bara lägga in en extra post i en pristabell och sedan med en smart sql-fråga ta fram senaset priset.

Om man ska ha en historiktabell så måste man varje gång man gör én prisförändring först läsa fram det gamla priset för att lagra det i en historiktabell och sen lägga in det nya priset i "den aktuella pristabellen" detta måste även göras med en transaktion så det går att backa om någon del inte går igenom. Med den enklare varianten så slipper man hantera transaktioner.

Vad vi pratar om för prestandaeffektivisering är endast frågan om millisekunder vid prisfrågan, det tycker i vart fall inte jag är värt den betydligt mer komplicerade strukturen som det lätt kan bli fel i, det är mindre risk att det blir fel om man bara lägger till en extra insert varje gång priset förändras.

Den mer komplexa lösningen kräver även mer "knowledgein the head" dvs kunskap hos den som administrerar databasen än den enklare lösnuingen där det är fokus på "Knowledge in the world" (dvs mer kunskap finns i databasen i sig själv).
Mjo, där har du nog rätt när jag tänker efter ett varv till.

Är det väldigt mycket läsningar och få uppdateringar av priset(vilket borde vara normalfallet) blir det dock en viss prestandavinst med "min" lösning. I de flesta sammanhang är det nog inte så mycket att bry sig om dock. Resultatet kan ju även cachas om prestanda är viktigt.
pelmered är inte uppkopplad   Svara med citatSvara med citat
Svara


Aktiva användare som för närvarande tittar på det här ämnet: 1 (0 medlemmar och 1 gäster)
 

Regler för att posta
Du får inte posta nya ämnen
Du får inte posta svar
Du får inte posta bifogade filer
Du får inte redigera dina inlägg

BB-kod är
Smilies är
[IMG]-kod är
HTML-kod är av

Forumhopp


Alla tider är GMT +2. Klockan är nu 13:01.

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