sql >> データベース >  >> RDS >> Mysql

Djangoviews.pyバージョンのSQL結合とマルチテーブルクエリ

    これらは不明確なテーブル名とフィールド名ですが、クエリは次のようになります。

    (Restaurant.objects.filter(city=8, 
         cuisine__cuisinetype__cuisine="Italian").distinct().order_by('name')[:20])
    

    ただし、そのデータベーススキーマに固定されていない限り、モデルは次のように表示されます。

    class CuisineType(models.Model):
        name = models.CharField(max_length=50)
        class Meta:
            db_table = 'cuisinetype'
    
    class Restaurants(models.Model):
        city = models.ForeignKey("City", null=True, blank=True) # Apparently defined elsewhere. Should be part of location?
        name = models.CharField(max_length=50)
        location = models.ForeignKey("Location", null=True, blank=True) # Apparently defined elsewhere.
        cuisines = models.ManyToManyField(CuisineType)
    

    その場合、クエリは次のようになります。

    Restaurant.objects.filter(city=8, cuisines__name="Italian").order_by('name')[:20]
    

    OK、コードに変更がないと仮定して、クエリを見ていきましょう。サブクエリから始めましょう。

    SELECT DISTINCT res_id FROM cuisine 
            JOIN    cuisinetype ON cuisine.cuisineid = cuisinetype.`cuisineid`
            WHERE   cuisinetype.`cuisine` = 'Italian'
    

    WHERE句を見ると、JOINが必要であることがわかります。結合を行うには、結合されたモデルの1つでリレーショナルフィールドを宣言する必要があります(Djangoは逆の関係を追加します。これに名前を付ける必要があります)。したがって、cuisine.cuisineidを照合します `cuisinetype.cuisineidで。それは恐ろしい名前です。

    これは多対多の関係であるため、ManyToManyFieldが必要です。 。さて、Cuisineを見てください モデル、それは本当にこのM2Mの結合テーブルです。 Djangoは、結合テーブルに2つのForeignKeyがあることを想定しています。 フィールド、1つはジョイントの両側を指します。通常、正気を保つためにこれを作成します。どうやらあなたはそれほど幸運ではありません。したがって、手動で接続する必要があります。

    「GID」フィールドはレコードの(役に立たない)IDフィールドのように見えるので、自動インクリメント整数であると仮定しましょう。 (確かに、CREATE TABLEコマンドを確認してください。)これで、Cuisineを書き直すことができます。 正気に近づく何かにモデル化する:

    class Cuisine(models.Model):
        cuisinegid = models.AutoField(primary_key=True, db_column='CuisineGID')
        cuisineid = models.ForeignKey("Cuisinetype", null=True, 
            db_column='CuisineID', blank=True)
        res_id = models.ForeignKey("Restaurant", null=True, db_column='Res_ID', 
            blank=True)
        class Meta:
            db_table = 'cuisine'
    

    モデルはまだ定義されていないため、モデル名は引用符で囲まれています(モデルはファイルの後半にあります)。これで、Djangoフィールド名が列名と一致する必要がなくなったので、より読みやすい名前に変更してみましょう。レコードIDフィールドは通常、単にidという名前です。 、および外部キーは通常、関連するものにちなんで名付けられます:

    class Cuisine(models.Model):
        id = models.AutoField(primary_key=True, db_column='CuisineGID')
        cuisine_type = models.ForeignKey("CuisineType", null=True, 
            db_column='CuisineID', blank=True)
        restaurant = models.ForeignKey("Restaurant", null=True, db_column='Res_ID', 
            blank=True)
        class Meta:
            db_table = 'cuisine'
    

    OK、ジョイントテーブルの定義は完了です。この間、同じものをCuisinetypeに適用してみましょう。 モデル。修正されたキャメルケースのクラス名に注意してください:

    class CuisineType(models.Model):
        id = models.AutoField(primary_key=True, db_column='CuisineID')
        name = models.CharField(max_length=50, db_column='Cuisine', blank=True)
        class Meta:
            db_table = 'cuisinetype'
    

    ついにRestaurantにたどり着きました モデル。名前は単数;であることに注意してください オブジェクトは1つのレコードのみを表します。

    dp_tableがないことに気づきました またはdb_column だから、私は手足に出かけて、Djangoがそれを作成していると推測しています。つまり、idを作成させることができます フィールドであり、コードから省略できます。 (そうでない場合は、他のモデルと同じように追加します。ただし、null許容レコードIDは使用しないでください。)そして、ここで料理のタイプがManyToManyFieldになります。 生活:

    class Restaurants(models.Model):
        city_id = models.ForeignKey(null=True, blank=True)
        name = models.CharField(max_length=50, blank=True)
        location = models.ForeignKey(null=True, blank=True)
        cuisine_types = models.ManyToManyField(CuisineType, through=Cuisine,
            null=True, blank=True)
    

    M2Mフィールドの名前は複数形であることに注意してください。これは、その関係が複数のレコードにつながるためです。

    このモデルに追加したいもう1つのことは、逆の関係の名前です。つまり、他のモデルからRestaurantに戻る方法 。これを行うには、related_nameを追加します パラメーター。それらが同じであることは珍しいことではありません。

    class Restaurant(models.Model):
        city_id = models.ForeignKey(null=True, blank=True, 
            related_name="restaurants")
        name = models.CharField(max_length=50, blank=True)
        location = models.ForeignKey(null=True, blank=True, 
            related_name="restaurants")
        cuisine_types = models.ManyToManyField(CuisineType, through=Cuisine,
            null=True, blank=True, related_name="restaurants")
    

    これでようやく設定されました。それでは、クエリを見てみましょう:

    SELECT  restaurants.`name`, restaurants.`address`, cuisinetype.`cuisine`
    FROM    restaurants
    JOIN    cuisinetype ON cuisinetype.cuisineid = restaurants.`cuisine`
    WHERE   city_id = 8 AND restaurants.id IN (
            SELECT DISTINCT res_id FROM cuisine 
            JOIN    cuisinetype ON cuisine.cuisineid = cuisinetype.`cuisineid`
            WHERE   cuisinetype.`cuisine` = 'Italian')
    ORDER BY restaurants.`name`
    LIMIT 20
    

    これはFROM restaurantsなので 、そのモデルのデフォルトのオブジェクトマネージャーであるobjectsから始めます。 :

    Restaurant.objects
    

    WHERE この場合の句はfilter()です 呼び出すので、最初の用語に追加します:

    Restaurant.objects.filter(city=8)
    

    主キー値またはCityを枯渇させることができます その用語の右側にあるオブジェクト。ただし、JOINが必要なため、クエリの残りの部分はより複雑になります。 。 Djangoでの結合は、リレーションフィールドを介した間接参照のように見えます。クエリでは、関連するフィールド名を二重アンダースコアで結合することを意味します:

    Restaurant.objects.filter(city=8, cuisine_type__name="Italian")
    

    Djangoは、Cuisineで宣言されているため、どのフィールドに参加するかを知っています。 through=Cuisineによって引き込まれるテーブル cuisine_typesのパラメータ 。また、M2M関係を経由しているため、サブクエリを実行することも認識しています。

    つまり、SQLは次と同等になります:

    SELECT  restaurants.`name`, restaurants.`address`
    FROM    restaurants
    WHERE   city_id = 8 AND restaurants.id IN (
            SELECT res_id FROM cuisine 
            JOIN    cuisinetype ON cuisine.cuisineid = cuisinetype.`cuisineid`
            WHERE   cuisinetype.`cuisine` = 'Italian')
    

    途中です。ここで、SELECT DISTINCTが必要です。 したがって、同じレコードの複数のコピーを取得することはありません:

    Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
    

    そして、表示する料理の種類を取り込む必要があります。結合テーブルにたどり着くだけで、関連するCuisineTypeを取得するにはさらにクエリを実行する必要があるため、ここでのクエリは非効率的であることがわかります。 記録。推測:Djangoはあなたをカバーしています。

    (Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
        .prefetch_related("cuisine_types"))
    

    Djangoは2つのクエリを実行します。1つはジョイントIDを取得するためのクエリで、もう1つは関連するCuisineTypeを取得するためのクエリです。 記録。その後、クエリ結果を介したアクセスはデータベースに戻る必要はありません。

    最後の2つは順序です:

    (Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
        .prefetch_related("cuisine_types").order_by("name"))
    

    そしてLIMIT

    (Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
        .prefetch_related("cuisine_types").order_by("name")[:20])
    

    そして、Pythonの2行にパックされたクエリ(および関連するクエリ)があります。この時点では、クエリは実行されていません。何かをする前に、テンプレートのようなものに入れる必要があります:

    def cuisinesearch(request, cuisine):
        return render_to_response('cuisinesearch.html', {
            'restaurants': (Restaurant.objects.filter(city=8, 
                 cuisine_type__name="Italian").distinct()
                 .prefetch_related("cuisine_types").order_by("name")[:20])
            })
    

    テンプレート:

    {% for restaurant in cuisinesearch %}
    <h2>{{ restaurant.name }}</h2>
    <div class="location">{{ restaurant.location }}</div>
    <h3>Cuisines:</h3>
    <ul class="cuisines">{% for ct in restaurant.cuisine_types.all %}
    <li>{{ ct.name }}</li>{% endfor %}
    </ul>
    {% endfor %}
    



    1. Javaのランダムな数字

    2. Oracle regexp_replaceによってスペースで区切られたリストから重複を削除するにはどうすればよいですか?

    3. 外部キーをNULLにしたり、重複させたりすることはできますか?

    4. SQLServerテーブルのスキーマとデータを比較するさまざまな方法