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
                            Lz

                            C ++ - Test 004. Pointers, Arrays and Loops

                            • Result:70points,
                            • Rating points1
                            РК

                            Qt - Test 001. Signals and slots

                            • Result:84points,
                            • Rating points4
                            Last comments
                            k
                            kmssrFeb. 9, 2024, 5:43 a.m.
                            Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                            Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                            EVA
                            EVADec. 25, 2023, 9:30 p.m.
                            Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
                            J
                            JonnyJoDec. 25, 2023, 7:38 p.m.
                            Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
                            G
                            GvozdikDec. 19, 2023, 8:01 a.m.
                            Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
                            Now discuss on the forum
                            NSProject
                            NSProjectJune 23, 2024, 11:48 p.m.
                            добавить qlineseries в функции А куда собстаенно делся Евгений раз на сайте такой бордак творится?
                            BlinCT
                            BlinCTMay 5, 2024, 3:46 p.m.
                            Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
                            Evgenii Legotckoi
                            Evgenii LegotckoiMay 3, 2024, 12:07 a.m.
                            Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
                            IscanderChe
                            IscanderCheApril 30, 2024, 2:22 p.m.
                            Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…
                            G
                            GarApril 22, 2024, 3:46 p.m.
                            Clipboard Как скопировать окно целиком в clipb?

                            Follow us in social networks