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

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

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

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

3

Do you like it? Share on social networks!

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

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

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

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

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

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

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

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

    P
    • March 29, 2023, 2:31 p.m.

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

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

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

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

    Добавляю annotate:

    1. uniqidset=set(JurnalDoc.objects.values_list('title__title','price').annotate(count=Sum('kol')))
    2. for i in uniqidset:
    3. 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.
        1. class Nom(models.Model):
        2. title = models.CharField(max_length=150, verbose_name='Наименование', unique=True )
        3. izm=models.ForeignKey(Unit,verbose_name='Ед.изм.',on_delete=models.PROTECT)
        4. category = models.ForeignKey(Category, verbose_name='Категория', on_delete=models.PROTECT)
        5. srok=models.IntegerField(blank=True,default=0)
        6. updated_at = models.DateTimeField(auto_now=True, verbose_name='Создан')
        7. created_at = models.DateTimeField(auto_now_add=True, verbose_name='Обновлен')
        8. def __str__(self):
        9. return self.title
        1. class JurnalDoc(models.Model):
        2.  
        3. oper=models.IntegerField(max_length=1,verbose_name='Операция')
        4. iddoc=models.ForeignKey(Jurnal,on_delete=models.PROTECT)
        5. title=models.ForeignKey(Nom,verbose_name='Наименование',on_delete=models.PROTECT)
        6. price=models.FloatField(verbose_name='Цена',default=0.0)
        7. kol = models.FloatField(verbose_name='Количество', default=0.0)
        8. podraz=models.ForeignKey(Podraz,on_delete=models.PROTECT,verbose_name='Подразделение')
        9. postav=models.ForeignKey(Postav,on_delete=models.PROTECT,verbose_name='Поставщик')
        10. obct=models.ForeignKey(Obct,on_delete=models.PROTECT,verbose_name='Объект')
        11. fio=models.ForeignKey(Fio,on_delete=models.PROTECT,verbose_name='Подотчетник')
        12. spis=models.ForeignKey(Spis,on_delete=models.PROTECT,null=True,blank=True,verbose_name='Причина списания')
        13. summa=models.FloatField(verbose_name='Сумма')
        14. nds=models.IntegerField(default=20,verbose_name='НДС')
        15. summawithnds=models.FloatField(verbose_name='Сумма с НДС')
        16. updated_at = models.DateTimeField(auto_now=True, verbose_name='Создан')
        17. created_at = models.DateTimeField(auto_now_add=True, verbose_name='Обновлен')
        18. uniqfield=models.CharField(max_length=250,verbose_name='Слаг')
        19. def __str__(self):
        20. return self.title
        21.  

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

          Evgenii Legotckoi
          • March 29, 2023, 3:23 p.m.

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

          1. uniqidset=set(JurnalDoc.objects.values_list('title','price').annotate(count=Sum('kol')))
          2. for i in uniqidset:
          3. 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.

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

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

              а надо

              1. 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.

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

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

                    Evgenii Legotckoi
                    • March 30, 2023, 11:14 a.m.

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

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

                    1. ef GetActualData(request):
                    2. uniqidset=JurnalDoc.objects.values('title','price').annotate(count=Sum('kol')))
                    3. for i in uniqidset:
                    4. print(i)
                    5. 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? А я думаю, почему это не работает.
                        Возможно проблема в отсутствии сортировки результатов. Попробуйте так

                        1. def GetActualData(request):
                        2. uniqidset=JurnalDoc.objects.values('title','price').annotate(count=Sum('kol')).order_by('title','price')
                        3. for i in uniqidset:
                        4. print(i)
                        5. 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
                            • Last comments
                            • Evgenii Legotckoi
                              March 9, 2025, 9:02 p.m.
                              К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
                            • VP
                              March 9, 2025, 4:14 p.m.
                              Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…
                            • ИМ
                              Nov. 22, 2024, 9:51 p.m.
                              Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                            • Evgenii Legotckoi
                              Oct. 31, 2024, 11:37 p.m.
                              Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
                            • A
                              Oct. 19, 2024, 5:19 p.m.
                              Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html