2017-03-27 1 views
-2

Da ich nicht in der Lage bin, einen Join zu erstellen, um erwartete Ergebnisse zu erzielen, habe ich angefangen zu denken, dass die ganze Architektur falsch ist.SQL SELECT JOIN und DB Architecture

Modelle (nur relevante Felder):

public class AspNetUsers // this is ASPNET default identity table modified 
    { 
     public string Id { get; set; } 
     public int GeoID { get; set; } // FK to GeoData PK 
     public string Email { get; set; } 

public partial class Product 
    { 
     public int ID { get; set; } 
     public string Name { get; set; } 
     public string Description { get; set; } 
     public int CategoryID { get; set; } // FK to Category PK 
     public string UserID { get; set; } // FK to AspNetUsers PK 

public partial class Category 
    { 
     public int ID { get; set; } 
     public string Name { get; set; } 

public class GeoData 
    { 
     public int ID { get; set; } 
     public DbGeography GeoLocation { get; set; } 

public class WishList 
    { 
     public int ID { get; set; } 
     public string userEmail { get; set; } // FK to AspnetUsers 'Email' 
     public int frequency { get; set; } 
     public int category { get; set; } // FK to Category PK 
     public int range { get; set; } 
     public int geoid { get; set; } // FK to Geodata PK 

Die Idee ist: Wir haben Produkte und Kategorien. Produkte sind benutzerbezogen. Die Benutzer-ID FK im Produktmodell berücksichtigt das, indem jedes Produkt mit seinem Besitzer in der AspNetUsers-Tabelle verknüpft wird, da jeder Benutzer sich registriert, sich über ASPnet-Identität anmeldet. Vor dem Einstellen ihrer Produkte müssen sich Benutzer selbst lokalisieren, damit ihre Produkte von anderen Benutzern bequem georeferenziert (gesucht) werden können. Die Geodaten-Tabelle berücksichtigt weltweite Koordinaten, Postleitzahlen, Ortsnamen usw. Nun zur WishList. Jeder Benutzer kann "n" Wunschlisten einrichten, die eine Kategorie festlegen, an der er interessiert ist, sowie einen Ort und einen Bereich von diesem Ort. Das Ergebnis der Whislist wird per SQL-Server per E-Mail an die Benutzer gesendet, die auf das Feld 'Häufigkeit' terminiert sind.

Ich bin mir nicht sicher, dass dies die bestmögliche db-Architektur ist. Manchmal beginnen Sie einfach mit einigen Bausteinen (AspNetUsers, Products, Categories) und fügen weitere Funktionen nach und nach hinzu. Wie dem auch sei .. Hier ist eine grundlegende SELECT, um die E-Mails zu erstellen, je nach Benutzer unterschiedlich Wunsch:

DECLARE C1 CURSOR READ_ONLY 
FOR 
SELECT [userEmail], [frequency], [category],[range], w.[geoid], [searchCity], u.UserName, g.[GeoLocation] 
FROM WishLists as w 
JOIN AspNetUsers as u ON w.userEmail = u.Email 
JOIN GeoData_IT as g ON g.ID = w.geoid 

OPEN C1; 
    FETCH NEXT FROM C1 INTO 
    @userEmail, @frequency, @category, @range, @geoid, @searchCity, @userName, @GeoLocation 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
    IF @geoid > 0 
      BEGIN 
       SELECT p.ID, c.Name, p.Name, g.PlaceName 
          FROM WishLists as w 
          RIGHT OUTER JOIN GeoData_IT AS g ON w.geoid = g.ID 
          JOIN AspNetUsers AS u ON g.ID = u.GeoID 
          JOIN Products as p on u.Id = p.UserID 
          JOIN Categories AS c ON p.CategoryID = c.ID 
          WHERE g.GeoLocation.STDistance(@GeoLocation) <= (@range*5000) 

      END 
      FETCH NEXT FROM C1 INTO 
      @userEmail, @frequency, @category, @range, @geoid, @searchCity, @userName, @GeoLocation 
    END 

CLOSE C1; 
DEALLOCATE C1; 
GO 

Ich bin nicht einmal die Einführung von ‚gesendet werden sollen Kategorie‘ Komplikation an dieser Stelle nur zu prüfen, ob der Wunschliste ein relevantes enthält Geoid (wenn Geoid> 0) versucht, für jede Wunschliste ein Produkt zu extrahieren, das zu Benutzern gehört, deren Geolokalisierung in dem gegebenen Bereich von dem gegebenen Geoid liegt. Aber ich bekomme ein doppeltes Ergebnis, zufällig nur auf einer bestimmten Wunschliste, und ich kann nicht herausfinden, warum.

Das sieht gut aus:

1 Nursery Loved Crib Bale Genoa 
2 Baby products Crib Genoa 
3 Baby products Cot Bed Genoa 
4 Feeding Circus Crib Bale Genoa 

Während diese Duplikate zeigt:

1555 Baby products this uaga product Recco 
1555 Baby products this uaga product Recco 
1556 Automotive uaga product Recco 
1556 Automotive uaga product Recco 

Ist die gesamte Architektur versagt oder nur die SELECT?

+0

Es gibt mehrere Probleme mit Ihrem Datenmodell einschließlich, warum eine Wunschliste ein Geoid hat und warum hat sie die Useremail statt userId ? Außerdem sollten Produkte keine UserId haben. Die Wunschliste sollte die ProductId haben. Soweit die Auswahl und warum Du Duplikate hast? Es könnte sein, dass 2 Wunschlisten für zwei verschiedene Benutzer das gleiche Geoid haben und dann würden Sie zwei Zeilen wie Ihre Ergebnisse sehen. – Anand

+0

1. Da userEmail in der AspNetUsers-Tabelle einzigartig ist, kann es als PK verwendet werden, wodurch die Einbindung von JOIN in vielen Fällen entfällt. 2. Es gibt eine Eins-zu-viele-Beziehung zwischen Benutzer-ID und Produkten. Sicher könnte ich die Architektur mit einer dritten Tabelle komplizieren: wäre das dringend ratsam? 3. Eine Wunschliste hat nichts mit einzelnen Produkten zu tun. Eine Wunschliste richtet sich auf einen Ort, einen Bereich von diesem Ort und schließlich auf eine Kategorie. Sicher, falls Benutzer nach zwei oder zwanzig identischen Wunschlisten fragen (nicht wirklich weise), werden sie nach identischen Ergebnissen, nach dem gleichen Ort, Entfernung und Kategorie fragen. – Luke

+0

Ich erkannte nach meinem Kommentar, dass die Wunschliste standortbasiert war; Ich kann auch die userEmail in der Wunschliste ignorieren. Über (3) in Ihrem Kommentar ist das Problem nicht, wenn Benutzer identische Wunschlisten anfordern, das Problem ist, wenn zwei verschiedene Benutzer eine Wunschliste für das gleiche Geoid haben, erhalten beide Benutzer doppelte Zeilen durch Ihre SELECT-Anweisung '' 'FROM WishLists als w RECHTER ÄUSSERER JOIN GeoData_IT AS g ON w.geoid = g.ID VERBINDEN AspNetUsers AS u ON g.ID = u.GeoID''', weil Sie den Beitritt in der E-Mail verpassen. Abgesehen davon macht die Beziehung zwischen Benutzer und Produkt für mich keinen Sinn. – Anand

Antwort

-1

Es war nichts falsch mit der Architektur, das Problem ist, dass die SELECT WishLists ID beim Abrufen nicht referenziert.Hier ist die korrigierte Code:

DECLARE C1 CURSOR READ_ONLY 
FOR 
SELECT w.ID, w.userEmail, w.category, w.range, w.geoid, w.searchCity, u.UserName, g.GeoLocation 
FROM WishLists as w JOIN AspNetUsers as u ON w.userEmail = u.Email JOIN GeoData_IT as g ON g.ID = w.geoid 
WHERE w.frequency = 1 order by w.ID 
OPEN C1; 
    FETCH NEXT FROM C1 INTO 
    @WishListID, @userEmail, @category, @range, @geoid, @searchCity, @userName, @GeoLocation 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
    if @geoid > 0 
      begin 
     if @category > 0 
       begin 
      SELECT p.ID, c.Name, p.Name, g.PlaceName 
          FROM Products as p 
          JOIN Categories AS c ON p.CategoryID = c.ID 
          JOIN AspNetUsers AS u ON p.UserID = u.Id 
          JOIN WishLists AS w ON @WishListID = w.ID 
          JOIN GeoData_IT AS g ON u.GeoID = g.ID 
          WHERE p.IsApproved = 1 AND p.IsDeleted = 0 AND p.DateExpire > convert(date, getdate()) 
          AND w.IsDeleted = 0 AND g.GeoLocation.STDistance(@GeoLocation) <= (@range*5000) 
          AND p.CategoryID = @category 
          order by p.ID 
        end -- category != 0 
      end -- @geoid > 0 
      FETCH NEXT FROM C1 INTO 
      @WishListID, @userEmail, @category, @range, @geoid, @searchCity, @userName, @GeoLocation 

     END 

CLOSE C1; 
DEALLOCATE C1; 

der Schlüssel Linie ist

JOIN WishLists AS w ON @WishListID = w.ID

+0

Diese SQL unterscheidet sich erheblich von der ursprünglichen (einschließlich der Beseitigung der richtigen Outer Join). Wie verbinden Sie die Benutzer wieder mit den Wunschlisten? Diese Auswahl ist immer noch falsch und problematisch. Und ** nein **, Ihre Architektur hat immer noch erhebliche Probleme. – Anand

+0

Danke Kumpel Ich möchte nur meine Dankbarkeit ausdrücken. Ich hätte es ohne all Ihre technischen Vorschläge nicht schaffen können. Es ist wirklich schwer zu sagen, welcher Ihrer Beiträge nützlicher war. Sie alle erzählen so viel von Ihrem Willen, anderen Nutzern dieser Community von den Höhen der tiefen Erfahrung und des Wissens zu helfen, die Sie so gerne geteilt haben. Du bist der beste! – Luke

+0

Gern geschehen; Ich tue dies in meiner Ausfallzeit und ich bin glücklich zu helfen :) – Anand

0

Wenn Sie beabsichtigen, gespeicherte Prozeduren oder Cursor zu verwenden (Cursor sind zu vermeiden, wann immer möglich, versuchen Sie, verschiedene Techniken wie CTEs zu verwenden), dann sollten Sie definitiv zuerst Datenbank gehen. Es gibt keine Vorteile für Sie, den Code-First-Ansatz zu verwenden.

Das heißt, Sie deklarieren offenbar keine Beziehungen zwischen Tabellen mit ICollection.

Darüber hinaus verwenden Sie keine Datenanmerkungen, um Primärschlüssel mit [KEY] zu bestimmen oder die Größe Ihrer Zeichenfolgenspalten mit [MAXLENGTH (n)] zu begrenzen. Ihre Tabellen sind ohne Primär- und Fremdschlüssel und alle Stringspalten sind NVARCHAR (MAX) als Ergebnis. Ihre Tische werden dadurch viel Platz einnehmen.

Was Sie bekommen, ist ein kartesisches Produkt und es wird erwartet. Wenn Sie dies vermeiden möchten, ohne sich intensiv mit der Logik Ihrer Abfrage zu befassen, verwenden Sie DISTINCT oder GROUP BY.

+0

Die 'partielle' Deklaration sollte vorschlagen, dass andere partielle Klassen für die Definition von Metadada verwendet werden. Trotzdem, vielen Dank für Ihre Anregungen. – Luke

+0

Ja, es gibt zwei Teilklassen, aber ohne den Rest des Implementierungscodes können wir Ihnen auch nicht wirklich helfen. Wie es ist, klingt dieser Beitrag eher wie ein "tu meinen Job für mich" als eine vernünftige theoretische Frage. – NicVerAZ