P
Pisych29 марта 2023 г. 8:59

Как подсчитать количество по условию?

Добрый день! К примеру, такая таблица:

Товары могут быть с одинаковыми названиями, но разными ценами. Есть поле uniq_field (название, конечно, неверное, оно не уникальное, но оно идентифицирует товар в зависимости от id и цены... Может, оно вообще не нужно :)
Как мне получить в запросе количество по каждому товару в зависимости от цены? То есть товару с таким то названием и по цене 100 руб. в наличии столько-то, а вот такой же, но по цене 200 - столько...
Я пробовал получать уникальный set с uniq_id, затем в цикле обходить всю таблицу для каждого значения set, подсчитывать количество и т.д. Но это же неправильно? Оно работает, но долго на больших таблицах. Есть ли способ лучше? Как сделать такой запрос с использованием Sum?
Спасибо!
В общем, в итоге должно получится нечто типа этого:

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

13
Evgenii Legotckoi
  • 29 марта 2023 г. 14:09

Добрый день.
Да, здесь нужно средствами запросов к базе данных работать. Здесь нужно использовать annotate и values

Не уверен, что напишу полностью правильно без тестирования на кошках, но должно выглядеть как-то так

tovars = Tovar.objects.values("id_tovar", "price").annotate(count=Sum("kol"))

По идее, это должно взять все одинаковые значения для id товара и его цены и проссумировать столбец kol. В результате будет создан временный queryset со структурой id_tovar price count

Его уже можно будет итерировать примерно так или подобный способом

for tovar in tovars:
    print(tovar.id_tovar, tovar.price, tovar.count)

Операции annotate тоже затратные по времени, но этим будет заниматься база данных, поэтому это должно в любом случае работать быстрее

    P
    • 29 марта 2023 г. 14:31

    Да я так и пробую.

    Вот я получаю set, тут вроде все как надо

     uniqidset=set(JurnalDoc.objects.values_list('title__title','price'))
        for i in uniqidset:
            print(i)
    

    Вывожу в консоль: все верно, нет повторяющихся значений...
    Quit the server with CTRL-BREAK.
    ('Товар2', 50.0)
    ('Товар1', 200.0)
    ('Товар 3', 100.0)
    ('Товар1', 100.0)

    Добавляю annotate:

     uniqidset=set(JurnalDoc.objects.values_list('title__title','price').annotate(count=Sum('kol')))
        for i in uniqidset:
            print(i)
    

    Упс!

    ('Товар2', 50.0, 30.0)
    ('Товар 3', 100.0, 45.0)
    ('Товар1', 100.0, 20.0)
    ('Товар2', 50.0, 20.0)
    ('Товар1', 200.0, 20.0)

    А я хотел бы получить
    ('Товар2', 50.0, 50.0)
    ('Товар 3', 100.0, 45.0)
    ('Товар1', 100.0, 20.0)
    ('Товар1', 200.0, 20.0)
    Как можно сделать? Спасибо!

      Evgenii Legotckoi
      • 29 марта 2023 г. 14:59

      Должно работать. Покажите код моделей, ибо меня смущает эта часть values_list('title__title' , по ходу у вас там всё несколько сложнее, чем вы показали изначально

        P
        • 29 марта 2023 г. 15:10
        class Nom(models.Model):
            title = models.CharField(max_length=150, verbose_name='Наименование', unique=True )
            izm=models.ForeignKey(Unit,verbose_name='Ед.изм.',on_delete=models.PROTECT)
            category = models.ForeignKey(Category, verbose_name='Категория', on_delete=models.PROTECT)
            srok=models.IntegerField(blank=True,default=0)
            updated_at = models.DateTimeField(auto_now=True, verbose_name='Создан')
            created_at = models.DateTimeField(auto_now_add=True, verbose_name='Обновлен')
            def __str__(self):
                return self.title
        
        class JurnalDoc(models.Model):
        
            oper=models.IntegerField(max_length=1,verbose_name='Операция')
            iddoc=models.ForeignKey(Jurnal,on_delete=models.PROTECT)
            title=models.ForeignKey(Nom,verbose_name='Наименование',on_delete=models.PROTECT)
            price=models.FloatField(verbose_name='Цена',default=0.0)
            kol = models.FloatField(verbose_name='Количество', default=0.0)
            podraz=models.ForeignKey(Podraz,on_delete=models.PROTECT,verbose_name='Подразделение')
            postav=models.ForeignKey(Postav,on_delete=models.PROTECT,verbose_name='Поставщик')
            obct=models.ForeignKey(Obct,on_delete=models.PROTECT,verbose_name='Объект')
            fio=models.ForeignKey(Fio,on_delete=models.PROTECT,verbose_name='Подотчетник')
            spis=models.ForeignKey(Spis,on_delete=models.PROTECT,null=True,blank=True,verbose_name='Причина списания')
            summa=models.FloatField(verbose_name='Сумма')
            nds=models.IntegerField(default=20,verbose_name='НДС')
            summawithnds=models.FloatField(verbose_name='Сумма с НДС')
            updated_at = models.DateTimeField(auto_now=True, verbose_name='Создан')
            created_at = models.DateTimeField(auto_now_add=True, verbose_name='Обновлен')
            uniqfield=models.CharField(max_length=250,verbose_name='Слаг')
            def __str__(self):
                return self.title
        
        

        Ну title наверное неудачное название поля... Логичнее было бы id_nomenklatura или id_nom... Но у меня оно так называется..

          Evgenii Legotckoi
          • 29 марта 2023 г. 15:23

          Хорошо, тогда должно быть достаточно использовать просто внешний ключ 'title' без поиска по заголовку в записи товара.
          Маловероятно, но что тогда выдаст это?

           uniqidset=set(JurnalDoc.objects.values_list('title','price').annotate(count=Sum('kol')))
              for i in uniqidset:
                  print(i)
          
            P
            • 29 марта 2023 г. 15:31

            (1, 200.0, 20.0)
            (3, 50.0, 20.0)
            (68, 100.0, 45.0)
            (3, 50.0, 30.0)
            (1, 100.0, 20.0)

              Илья Чичак
              • 29 марта 2023 г. 15:33

              это потому, что у вас

              uniqidset=set(JurnalDoc.objects.values_list('title__title','price').annotate(count=Sum('kol')))
              

              а надо

              uniqidset=set(JurnalDoc.objects.values('title__title','price').annotate(count=Sum('kol')))
              

              values вместо values_list

                P
                • 29 марта 2023 г. 15:35

                большое спасибо. завтра проверю. сейчас уже нет возможности:(

                  P
                  • 30 марта 2023 г. 7:15

                  А так выдает ошибку:

                  def GetActualData(request):
                      uniqidset=set(JurnalDoc.objects.values('title','price').annotate(count=Sum('kol')))
                      for i in uniqidset:
                          print(i)
                      return HttpResponse('OK')
                  

                    Evgenii Legotckoi
                    • 30 марта 2023 г. 11:14

                    Ну а подумать над ошибкой? ;-)

                    У вас dict в set не переводится, да и не нужно это там

                    ef GetActualData(request):
                        uniqidset=JurnalDoc.objects.values('title','price').annotate(count=Sum('kol')))
                        for i in uniqidset:
                            print(i)
                        return HttpResponse('OK')
                    
                      P
                      • 30 марта 2023 г. 12:34

                      А я уже третий день думаю :))
                      Я так тоже пробовал, вот результат:
                      {'title__title': 'Товар 3', 'price': 100.0, 'count': 45.0}
                      {'title__title': 'Товар1', 'price': 100.0, 'count': 20.0}
                      {'title__title': 'Товар2', 'price': 50.0, 'count': 30.0}
                      {'title__title': 'Товар2', 'price': 50.0, 'count': 20.0}
                      {'title__title': 'Товар1', 'price': 200.0, 'count': 20.0}
                      {'title__title': 'Товар1', 'price': 100.0, 'count': 20.0}
                      [30/Mar/2023 13:24:33] "GET /GetActualData/ HTTP/1.1" 200 2

                      А надо не два вот этих:
                      {'title__title': 'Товар2', 'price': 50.0, 'count': 30.0}
                      {'title__title': 'Товар2', 'price': 50.0, 'count': 20.0}
                      а один:
                      {'title__title': 'Товар2', 'price': 50.0, 'count': 50.0}
                      Вообще мне это нужно вот для чего - эти данные потом будут передаваться в option селекта и оператор при выборе будет видеть, сколько у него осталось товара по такой-то цене... Чтобы лишнего не отпустил:). Да и вообще для отчетов потом нужно... Вот и хочу такой хитрый запрос сделать. Я бы плюнул, создал бы временную таблицу, и записывал бы туда остатки. Циклом опять же... Но вот интересно, можно ли в принципе такой запрос сделать. Вроде как на Postgress есть distinct по определенному полю, но у меня sqlite :(
                      Может, есть все таки идеи, как это можно?
                      Спасибо!

                        Evgenii Legotckoi
                        • 30 марта 2023 г. 12:42
                        • Ответ был помечен как решение.

                        То есть у вас SQLite? А я думаю, почему это не работает.
                        Возможно проблема в отсутствии сортировки результатов. Попробуйте так

                        def GetActualData(request):
                            uniqidset=JurnalDoc.objects.values('title','price').annotate(count=Sum('kol')).order_by('title','price')
                            for i in uniqidset:
                                print(i)
                            return HttpResponse('OK')
                        
                          P
                          • 30 марта 2023 г. 12:50
                          • (ред.)

                          Да! Вот так работает! Огромное Вам спасибо!
                          ........

                            Комментарии

                            Только авторизованные пользователи могут публиковать комментарии.
                            Пожалуйста, авторизуйтесь или зарегистрируйтесь
                            S

                            C++ - Тест 001. Первая программа и типы данных

                            • Результат:53баллов,
                            • Очки рейтинга-4
                            S

                            C++ - Тест 003. Условия и циклы

                            • Результат:57баллов,
                            • Очки рейтинга-2
                            g

                            C++ - Тест 005. Структуры и Классы

                            • Результат:100баллов,
                            • Очки рейтинга10
                            Последние комментарии
                            Evgenii Legotckoi
                            Evgenii Legotckoi25 мая 2023 г. 9:49
                            Как написать игру на Qt - Урок 2. Анимация героя игры (2D) Код на строчка 184-198 вызывает перерисовку области на каждый 4-й такт счётчика. По той логике не нужно перерисовывать объект постоянно, достаточно реже, чем выполняется игровой слот. А слот вып…
                            J
                            JonnyJo21 мая 2023 г. 15:49
                            Как написать игру на Qt - Урок 2. Анимация героя игры (2D) Евгений, благодарю! Всё равно не совсем понимаю :( Если муха двигает ножками только при нажатии клавиш перемещение, то что, собственно, делает код со строк 184-198 в triangle.cpp? В этих строчка…
                            Evgenii Legotckoi
                            Evgenii Legotckoi21 мая 2023 г. 10:57
                            Как написать игру на Qt - Урок 2. Анимация героя игры (2D) Добрый день. slotGameTimer срабатывает по таймеру и при каждой сработке countForSteps увеличивается на 1, это не зависит от нажатия клавиш, нажатая клавиша лишь определяет положение ножек, котор…
                            J
                            JonnyJo20 мая 2023 г. 16:27
                            Как написать игру на Qt - Урок 2. Анимация героя игры (2D) Евгений, здравствуйте! Подскажите, а почему при нажатии одной клавиши переменная countForSteps увеличивается не на 1, на 4, ведь одно действие даёт увеличение этой переменной только на единицу? …
                            Сейчас обсуждают на форуме
                            Evgenii Legotckoi
                            Evgenii Legotckoi16 апреля 2023 г. 9:07
                            Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Да, это возможно. Но подобные вещи лучше запускать через celery. То есть drf принимает команду, и после этого регистрирует задачу в celery, котроый уже асинхронно всё это выполняет. В противном …
                            АБ
                            Алексей Бобров15 декабря 2021 г. 0:03
                            Sorting the added QML elements in the ListModel I am writing an alarm clock in QML, I am required to sort the alarms in ascending order (depending on the date or time (if there are several alarms on the same day). I've done the sorting …
                            Evgenii Legotckoi
                            Evgenii Legotckoi29 марта 2023 г. 9:11
                            Замена поля ManyToMany Картинки точно нужно хранить в медиа директории на сервере, а для обращения использовать ImageField. Который будет хранить только путь к изображению на сервере. Хранить изображения в базе данных…
                            Evgenii Legotckoi
                            Evgenii Legotckoi24 апреля 2023 г. 8:22
                            Пакеты данных между сервером и клиентами Привет. Если классы имеют что-то общее в полях, а также общую идеологию и их можно вписать в иерархию наследования, то в первую очередь переписать так, чтобы один базовый класс объединял в…

                            Следите за нами в социальных сетях