SQL and Django ORM

์ฐธ๊ณ ๋ฌธ์„œ

Making queries | Django documentation | Django

QuerySet API reference | Django documentation | Django

Aggregation | Django documentation | Django

๊ธฐ๋ณธ ์ค€๋น„ ์‚ฌํ•ญ

  • django app

    • django_extensions ์„ค์น˜

    • users app ์ƒ์„ฑ

    • csv ํŒŒ์ผ์— ๋งž์ถฐ models.py ์ž‘์„ฑ ๋ฐ migrate

      python manage.py sqlmigrate users 0001
  • db.sqlite3 ํ™œ์šฉ ๋ฐ ๋ฐ์ดํ„ฐ ๋ฐ˜์˜

    • sqlite3 ์‹คํ–‰

      $ ls
      db.sqlite3 manage.py ...
      $ sqlite3 db.sqlite3
    • csv ํŒŒ์ผ data ๋กœ๋“œ

      sqlite > .tables
      auth_group                  django_admin_log
      auth_group_permissions      django_content_type
      auth_permission             django_migrations
      auth_user                   django_session
      auth_user_groups            auth_user_user_permissions
      users_user
      sqlite > .mode csv
      sqlite > .import users.csv users_user
      sqlite > SELECT COUNT(*) FROM users_user;
      1000
  • ํ™•์ธ

    • sqlite3์—์„œ ์Šคํ‚ค๋งˆ ํ™•์ธ

      sqlite > .schema users_user

๋ฌธ์ œ

์•„๋ž˜์˜ ๋ฌธ์ œ๋“ค์„ sql๋ฌธ๊ณผ ๋Œ€์‘๋˜๋Š” orm์„ ์ž‘์„ฑ ํ•˜์„ธ์š”.

Table ์ƒ์„ฑ

  • django

    # django
    class User(models.Model):
        first_name = models.CharField(max_length=10)
        last_name = models.CharField(max_length=10)
        age = models.IntegerField()
        country = models.CharField(max_length=10)
        phone = models.CharField(max_length=15)
        balance = models.IntegerField()
        
    # python manage.py makemigrations
    # python manage.py migrate
  • SQL

    • sql.sqlite3์— ๋™์ผํ•˜๊ฒŒ ํ…Œ์ด๋ธ” ์ƒ์„ฑ

      --sql
      
      CREATE TABLE IF NOT EXISTS "users_user" (
          "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
          "first_name" varchar(10) NOT NULL, 
          "last_name" varchar(10) NOT NULL, 
          "age" integer NOT NULL, 
          "country" varchar(10) NOT NULL, 
          "phone" varchar(15) NOT NULL, 
          "balance" integer NOT NULL
      );

๊ธฐ๋ณธ CRUD ๋กœ์ง

  1. ๋ชจ๋“  user ๋ ˆ์ฝ”๋“œ ์กฐํšŒ

    # orm
    
    users = User.objects.all()
    type(users)
    # => django.db.models.query.QuerySet
    print(users.query)
    # queryset๋งŒ sql๋ฌธ ์ถœ๋ ฅ ๊ฐ€๋Šฅ
    # => SELECT "users_user"."id", "users_user"."first_name", "users_user"."last_name", "users_user"."age", "users_user"."country", "users_user"."phone", "users_user"."balance" FROM "users_user"
    -- sql
    
    SELECT*FROM users_user;
  2. user ๋ ˆ์ฝ”๋“œ ์ƒ์„ฑ

    # orm
    
    User.objects.create (
     first_name='๊ตฌ๋ฆ„',
        last_name='๊น€',
        age=100,
        country='์ œ์ฃผ๋„',
        phone='010-1234-5678',
        balance=10000000
    )
    -- sql
    
    INSERT INTO users_user(first_name, last_name, age, country, phone, balance)
    VALUES ('์ฃผํ˜„', '๊น€', 26, '๊ฒฝ๊ธฐ๋„', '010-0000-0000', 100000000000);
    • ํ•˜๋‚˜์˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋นผ๊ณ  ์ž‘์„ฑ ํ›„ NOT NULL constraint ์˜ค๋ฅ˜๋ฅผ orm๊ณผ sql์—์„œ ๋ชจ๋‘ ํ™•์ธ ํ•ด๋ณด์„ธ์š”.

      # orm
      IntegrityError: NOT NULL constraint failed: users_user.age
      -- sql
      Error: NOT NULL constraint failed: users_user.last_name
  3. ํ•ด๋‹น user ๋ ˆ์ฝ”๋“œ ์กฐํšŒ

    # orm
    
    User.objects.get(id=100)
    #=> <User: User object (100)>
    • get์€ query ๊ฒฐ๊ณผ๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•˜๋‚˜์—ฌ์•ผ ํ•œ๋‹ค. (์ด์™ธ์—๋Š” ๋ชจ๋‘ return error!)

      User.object.get(last_name='๊น€')
      # MultipleObjectsReturned: get() returned more than one User -- it returned 24!
      User.objects.get(id=1000)
      # DoesNotExists: User matching query does not exists.
      -- sql

    SELECT * FROM users_user WHERE id = 100;

  4. ํ•ด๋‹น user ๋ ˆ์ฝ”๋“œ ์ˆ˜์ •

    # orm
    
    user = User.objects.get(id=100)
    user.last_name='์„ฑ'
    user.save()
    -- sql
    
    UPDATE users_user
    SET last_name='์ตœ'
    WHERE id=100;
  5. ํ•ด๋‹น user ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ

    # orm
    
    User.objects.get(id=101).delete()
    -- sql
    DELETE FROM users_user
    WHERE id = 102;

์กฐ๊ฑด์— ๋”ฐ๋ฅธ ์ฟผ๋ฆฌ๋ฌธ

  1. ์ „์ฒด ์ธ์› ์ˆ˜

    # orm
    
    # ver1)
    User.objects.all().count()
    # ver2) - ์ด๊ฑธ ์“ฐ๋ผ
    User.objects.count()
    -- sql
    
    -- ver1)
    SELECT COUNT(*) FROM users_user;
    
    -- ver2)
    SELECT COUNT(id) FROM users_user;
  2. ๋‚˜์ด๊ฐ€ 30์ธ ์‚ฌ๋žŒ์˜ ์ด๋ฆ„

    # orm
    User.objects.filter(age=30)
    #=> <QuerySet [<User: User object (5)>, <User: User object (57)>, <User: User object (60)>]>
    
    User.objects.filter(age=30).values('first_name')
    #=> <QuerySet [{'first_name': '์˜ํ™˜'}, {'first_name': '๋ณด๋žŒ'}, {'first_name': '์€์˜'}]>
    
    type(User.objects.filter(age=30).values('first_name')[0])  
    #=> dict
    
    print(User.objects.filter(age=30).values('first_name').query)  
    #=> SELECT "users_user"."first_name" FROM "users_user" WHERE "users_user"."age" = 30
    -- sql
    
    SELECT first_name FROM users_user
    WHERE age = 30;
  3. ๋‚˜์ด๊ฐ€ 30์‚ด ์ด์ƒ์ธ ์‚ฌ๋žŒ์˜ ์ธ์› ์ˆ˜

    ๋Œ€์†Œ๊ด€๊ณ„ __gte : >=

    __gt : >

__lte : <=

__lt : <

# orm
User.objects.filter(age__gte=30)
print(User.objects.filter(age__gte=30).query)
# SELECT "users_user"."id", "users_user"."first_name", "users_user"."last_name", "users_user"."age", "users_user"."country", "users_user"."phone", "users_user"."balance" FROM "users_user" WHERE "users_user"."age" >= 30
User.objects.filter(age__gte=30).count()
-- sql

SELECT COUNT(*) FROM users_user
WHERE age>=30;
  1. ๋‚˜์ด๊ฐ€ 30์ด๋ฉด์„œ ์„ฑ์ด ๊น€์”จ์ธ ์‚ฌ๋žŒ์˜ ์ธ์› ์ˆ˜

    # orm -1 
    User.objects.filter(age=30).filter(last_name='๊น€').count()
    # orm -2
    User.objects.filter(age=30, last_name='๊น€').count()
    # query
    print(User.objects.filter(age=30).filter(last_name='๊น€').query)
    # => SELECT "users_user"."id", "users_user"."first_name", "users_user"."last_name", "users_user"."age", "users_user"."country", "users_user"."phone", "users_user"."balance" FROM "users_user" WHERE ("users_user"."age" = 30 AND "users_user"."last_name" = ๊น€)
    -- sql
    
    SELECT COUNT(*) from users_nuser
    WHERE age = 30 AND last_name ='๊น€';
  2. ์ง€์—ญ๋ฒˆํ˜ธ๊ฐ€ 02์ธ ์‚ฌ๋žŒ์˜ ์ธ์› ์ˆ˜

    https://docs.djangoproject.com/en/2.2/topics/db/queries/#escaping-percent-signs-and-underscores-in-like-statements

    # orm
    
    User.objects.filter(phone__startswith='02-').count()
    
    # query
    print(User.objects.filter(phone__startswith='02-').query)
    #=> SELECT "users_user"."id", "users_user"."first_name", "users_user"."last_name", "users_user"."age", "users_user"."country", "users_user"."phone", "users_user"."balance" FROM "users_user" WHERE "users_user"."phone" LIKE 02-% ESCAPE '\'
    -- sql
    
    SELECT COUNT(*) FROM users_user
    WHERE phone LIKE '02-%';
  3. ๊ฑฐ์ฃผ ์ง€์—ญ์ด ๊ฐ•์›๋„์ด๋ฉด์„œ ์„ฑ์ด ํ™ฉ์”จ์ธ ์‚ฌ๋žŒ์˜ ์ด๋ฆ„

    # orm
    -- sql

์ •๋ ฌ ๋ฐ LIMIT, OFFSET

  1. ๋‚˜์ด๊ฐ€ ๋งŽ์€ ์‚ฌ๋žŒ 10๋ช…

    # orm
    User.objects.order_by('-age')[:10]
    
    # query
    print(User.objects.order_by('-age')[:10].query)
    #=> SELECT "users_user"."id", "users_user"."first_name", "users_user"."last_name", "users_user"."age", "users_user"."country", "users_user"."phone", "users_user"."balance" FROM "users_user" ORDER BY "users_user"."age" DESC  LIMIT 10
    -- sql
    
    SELECT * FROM users_user
    ORDER BY age DESC
    LIMIT 10;
    
    id | first_name | last_name | age | country | phone | balance
    1 | ์ •ํ˜ธ | ์œ  | 40 | ์ „๋ผ๋ถ๋„ | 016-7280-2855 | 370
    4 | ๋ฏธ๊ฒฝ | ์žฅ | 40 | ์ถฉ์ฒญ๋‚จ๋„ | 011-9079-4419 | 250000
    28 | ์„ฑํ˜„ | ๋ฐ• | 40 | ๊ฒฝ์ƒ๋‚จ๋„ | 011-2884-6546 | 580000
  2. ์ž”์•ก์ด ์ ์€ ์‚ฌ๋žŒ 10๋ช… (์˜ค๋ฆ„์ฐจ์ˆœ)

    # orm
    User.objects.order_by('balance')[:10]
    -- sql
    SELECT * FROM users_user
    ORDER BY balance ASC
    LIMIT 10;
  3. ์„ฑ, ์ด๋ฆ„ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ˆœ์œผ๋กœ 5๋ฒˆ์งธ ์žˆ๋Š” ์‚ฌ๋žŒ

    # orm
    
    User.objects.order_by('-last_name', '-first_name')[4]
    #=>  <User: User object (67)>
    -- sql
    
    SELECT * FROM users_user
    ORDER BY last_name DESC, first_name DESC
    LIMIT 1 OFFSET 4;
    
    id | first_name | last_name | age | country | phone | balance
    67 | ๋ณด๋žŒ | ํ—ˆ | 28 | ์ถฉ์ฒญ๋ถ๋„ | 016-4392-9432 | 82000

ํ‘œํ˜„์‹

ํ‘œํ˜„์‹์„ ์œ„ํ•ด์„œ๋Š” aggregate ๋ฅผ ์•Œ์•„์•ผํ•œ๋‹ค.

  1. ์ „์ฒด ํ‰๊ท  ๋‚˜์ด

    # orm
    
    from django.db.models import Avg
    User.objects.aggregate(Avg('age'))
    #=> {'age__avg': 28.23}
    -- sql
    
    SELECT AVG(age) FROM users_user;
    AVG(age)
    28.23
  2. ๊น€์”จ์˜ ํ‰๊ท  ๋‚˜์ด

    # orm
    
    from django.db.models import Avg
    User.objects.filter(last_name='๊น€').aggregate(Avg('age'))
    -- sql
    
    SELECT AVG(age) FROM users_user
    WHERE last_name = '๊น€';
  3. ๊ณ„์ขŒ ์ž”์•ก ์ค‘ ๊ฐ€์žฅ ๋†’์€ ๊ฐ’

    # orm
    
    from django.db.models import Max
    User.objects.aggregate(Max('balance'))
    -- sql
    
    SELECT MAX(balance) FROM users_user;
  4. ๊ณ„์ขŒ ์ž”์•ก ์ด์•ก

    # orm
    
    from django.db.models import Sum
    User.objects.aggregate(Sum('balance'))
    -- sql
    
    SELECT SUM(balance) FROM users_user;

Group by

annotate๋Š” ๊ฐœ๋ณ„ item์— ์ถ”๊ฐ€ ํ•„๋“œ๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค. ์ถ”ํ›„ 1:N ๊ด€๊ณ„์—์„œ ํ™œ์šฉ๋œ๋‹ค.

  1. ์ง€์—ญ๋ณ„ ์ธ์› ์ˆ˜

    # orm
    
    User.objects.values('country')
     # <QuerySet [{'country': '์ „๋ผ๋ถ๋„'}, {'country': '๊ฒฝ์ƒ๋‚จ๋„'}, {'country': '์ „๋ผ๋‚จ๋„'}, ...
    from django.db.models import Count
    User.objects.values('country').annotate(Count('country'))
    # <QuerySet [{'country': '๊ฐ•์›๋„', 'country__count': 14}, {'country': '๊ฒฝ๊ธฐ๋„', 'country__count': 9}, {'country': '๊ฒฝ์ƒ๋‚จ๋„', 'country__count': 9}, {'country': '๊ฒฝ์ƒ๋ถ๋„', 'country__count': 15}, {'country': '์ „๋ผ๋‚จ๋„', 'country__count': 10}, {'country': '์ „๋ผ๋ถ๋„', 'country__count': 11}, {'country': '์ œ์ฃผํŠน๋ณ„์ž์น˜๋„', 'country__count': 9}, {'country': '์ถฉ์ฒญ๋‚จ๋„', 'country__count': 9}, {'country': '์ถฉ์ฒญ๋ถ๋„', 'country__count': 14}]>
    -- sql
    
    SELECT country, COUNT(country) FROM users_user
    GROUP BY country;
    
    country | COUNT(country)
    ๊ฐ•์›๋„ | 14
    ๊ฒฝ๊ธฐ๋„ | 9
    ๊ฒฝ์ƒ๋‚จ๋„ | 9
    ๊ฒฝ์ƒ๋ถ๋„ | 15
    ์ „๋ผ๋‚จ๋„ | 10
    ์ „๋ผ๋ถ๋„ | 11
    ์ œ์ฃผํŠน๋ณ„์ž์น˜๋„ | 9
    ์ถฉ์ฒญ๋‚จ๋„ | 9
    ์ถฉ์ฒญ๋ถ๋„ | 14

Last updated

Was this helpful?