P
PisychMarch 29, 2023, 8:59 a.m.

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

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

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

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

13
Evgenii Legotckoi
  • March 29, 2023, 2:09 p.m.

Добрый день.
Да, здесь нужно средствами запросов к базе данных работать. Здесь нужно использовать 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
    • March 29, 2023, 2:31 p.m.

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

    Вот я получаю 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
      • March 29, 2023, 2:59 p.m.

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

        P
        • March 29, 2023, 3:10 p.m.
        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
          • March 29, 2023, 3:23 p.m.

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

           uniqidset=set(JurnalDoc.objects.values_list('title','price').annotate(count=Sum('kol')))
              for i in uniqidset:
                  print(i)
          
            P
            • March 29, 2023, 3:31 p.m.

            (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)

              Илья Чичак
              • March 29, 2023, 3:33 p.m.

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

              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
                • March 29, 2023, 3:35 p.m.

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

                  P
                  • March 30, 2023, 7:15 a.m.

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

                  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
                    • March 30, 2023, 11:14 a.m.

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

                    У вас 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
                      • March 30, 2023, 12:34 p.m.

                      А я уже третий день думаю :))
                      Я так тоже пробовал, вот результат:
                      {'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
                        • March 30, 2023, 12:42 p.m.
                        • The answer was marked as a solution.

                        То есть у вас 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
                          • March 30, 2023, 12:50 p.m.
                          • (edited)

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

                            Comments

                            Only authorized users can post comments.
                            Please, Log in or Sign up
                            S

                            C++ - Test 001. The first program and data types

                            • Result:53points,
                            • Rating points-4
                            S

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

                            • Result:57points,
                            • Rating points-2
                            g

                            C++ - Test 005. Structures and Classes

                            • Result:100points,
                            • Rating points10
                            Last comments
                            J
                            JonnyJoMay 25, 2023, 2:24 p.m.
                            How to make game using Qt - Lesson 2. Animation game hero (2D) Евгений, благодарю!
                            Evgenii Legotckoi
                            Evgenii LegotckoiMay 25, 2023, 4:49 a.m.
                            How to make game using Qt - Lesson 2. Animation game hero (2D) Код на строчка 184-198 вызывает перерисовку области на каждый 4-й такт счётчика. По той логике не нужно перерисовывать объект постоянно, достаточно реже, чем выполняется игровой слот. А слот вып…
                            J
                            JonnyJoMay 21, 2023, 10:49 a.m.
                            How to make game using Qt - Lesson 2. Animation game hero (2D) Евгений, благодарю! Всё равно не совсем понимаю :( Если муха двигает ножками только при нажатии клавиш перемещение, то что, собственно, делает код со строк 184-198 в triangle.cpp? В этих строчка…
                            Evgenii Legotckoi
                            Evgenii LegotckoiMay 21, 2023, 5:57 a.m.
                            How to make game using Qt - Lesson 2. Animation game hero (2D) Добрый день. slotGameTimer срабатывает по таймеру и при каждой сработке countForSteps увеличивается на 1, это не зависит от нажатия клавиш, нажатая клавиша лишь определяет положение ножек, котор…
                            J
                            JonnyJoMay 20, 2023, 11:27 a.m.
                            How to make game using Qt - Lesson 2. Animation game hero (2D) Евгений, здравствуйте! Подскажите, а почему при нажатии одной клавиши переменная countForSteps увеличивается не на 1, на 4, ведь одно действие даёт увеличение этой переменной только на единицу? …
                            Now discuss on the forum
                            Evgenii Legotckoi
                            Evgenii LegotckoiApril 16, 2023, 4:07 a.m.
                            Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Да, это возможно. Но подобные вещи лучше запускать через celery. То есть drf принимает команду, и после этого регистрирует задачу в celery, котроый уже асинхронно всё это выполняет. В противном …
                            АБ
                            Алексей БобровDec. 14, 2021, 7:03 p.m.
                            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 LegotckoiMarch 29, 2023, 4:11 a.m.
                            Замена поля ManyToMany Картинки точно нужно хранить в медиа директории на сервере, а для обращения использовать ImageField. Который будет хранить только путь к изображению на сервере. Хранить изображения в базе данных…
                            Evgenii Legotckoi
                            Evgenii LegotckoiApril 24, 2023, 3:22 a.m.
                            Пакеты данных между сервером и клиентами Привет. Если классы имеют что-то общее в полях, а также общую идеологию и их можно вписать в иерархию наследования, то в первую очередь переписать так, чтобы один базовый класс объединял в…

                            Follow us in social networks