Страницы

Поиск по вопросам

Показаны сообщения с ярлыком count. Показать все сообщения
Показаны сообщения с ярлыком count. Показать все сообщения

понедельник, 3 февраля 2020 г.

Удалить уникальные значения из list

#python #list #count


Подобная тема была, но ответ мне не ясен. Почему удаляет из списка не все значения?

data = [1, 2, 3, 4, 5, 6]
for i in data:
     if data.count(i) == 1:
           data.remove(i)
print data


Печатает [2,4,6]
    


Ответы

Ответ 1



Самое главное, что Вы должны запонить - никогда не изменяйте размер массива во время прохождения по нему. Давайте посмотрим, как изменение размера массива влияет на логику цикла: In [3]: l = list(range(6)) In [4]: for x in l: ...: print(x) ...: l.remove(x) ...: 0 2 4 Взглянем на это через призму замечательных ASCII рисунков: +---+---+---+---+---+---+---+ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | <- l +---+---+---+---+---+---+---+ ^ x Выведем на печать x и удалим его из списка: # print(0) +---+---+---+---+---+---+ | 1 | 2 | 3 | 4 | 5 | 6 | <- l +---+---+---+---+---+---+ ^ x Переместимся на следующий элемент, как завещал нам великий Guido van Rossum: +---+---+---+---+---+---+ | 1 | 2 | 3 | 4 | 5 | 6 | <- l +---+---+---+---+---+---+ ^ x Для закрепления, повторим действия: напечатаетаем, удалим и перейдем на следующую итерацию цикла: # print(2) +---+---+---+---+---+ | 1 | 3 | 4 | 5 | 6 | <- l +---+---+---+---+---+ ^ x (до перехода) +---+---+---+---+---+ | 1 | 3 | 4 | 5 | 6 | <- l +---+---+---+---+---+ ^ x (после перехода) Очевидно, что изменяя размер массива во время итерирования по нему, на свет рождается еще одно маленькое зло, которое может привести (и приводит) к ошибкам. Самый короткий рабочий эквивалент данного цикла представил @andreymal: data = [x for x in data if data.count(x) > 1] Но у представленных решений есть один общий недостаток - они имеют квадратичную сложность. In [9]: data = list(range(10000)) In [10]: %timeit [x for x in data if data.count(x) > 1] 1 loops, best of 3: 1.66 s per loop Стандартная библиотека Python предоставляет класс Counter, который подсчитает количество вхождений каждого элемента. Таким образом, скорость получится линейной (строго говоря, амортизированно линейной): In [11]: from collections import Counter In [12]: def f(xs): ....: counter = Counter(xs) ....: return [x for x in xs if counter[x] > 1] ....: In [13]: %timeit f(range(10000)) 100 loops, best of 3: 2.33 ms per loop

Ответ 2



Как писали в предыдущем ответе, массив меняется, а индекс не меняется, по сути получается лишнее смещение на следующий элемент при удалении другого элемента. Когда мне лень мудрить, а список чистить надо, я создаю копию массива: for i in tuple(data): if data.count(i) == 1: data.remove(i) (tuple вместо list, потому что он, говорят, производительнее) Когда мне мудрить не лень, я могу завести отдельный список под удаляемые элементы: rm = [] for i in data: if data.count(i) == 1: rm.append(i) for x in rm: data.remove(i) Когда я вспоминаю про существование генераторных выражений, я пишу вариант-однострочник: data = [x for x in data if data.count(x) > 1] Четвёртый известный мне вариант приведён в другом ответе.

Ответ 3



если добавить печать data = [1, 2, 3, 4, 5, 6] for i in data: print i if data.count(i) == 1: data.remove(i) print data получим 1 3 5 видимо в питоне, когда вы удаляете элемент, индекс остается, и вы шагаете через один. т.е. после удаления надо опять с тем же индексом проверять элемент. Проще всего запусть цикл по убыванию индекса вот так удалим всё (мой первый код на питоне :), наверняка можно красивее) data = [1, 2, 3, 4, 5, 6] i = len(data)-1 while i>=0 : if data.count(data[i]) == 1: data.remove(data[i]) i = i-1 print data

вторник, 10 декабря 2019 г.

Множественное использование count в Linq C#

#c_sharp #linq #lambda #count


Встала задача сделать выборку нескольких Count-значений из 2-ух таблиц базы данных.
Решил сделать это с помощью лямбда-выражений. На выходе получилось следующая реализация:

var counts = _context.Users.Select(user => new
{
   TotalUsers = _context.Users.Count(),
   TotalDeparments = _context.Depart.Count(),
   PeopleOver20YE= _context.Users.Count(c => DateTime.Now.Year - c.YearOfBirth >= 20),
   PeopleUnder20YE = _context.Users.Count(c => DateTime.Now.Year - c.YearOfBirth < 20)
}).First();


Это работает, но, очевидно, реализаия плохая. Как сделать нормальную count выборку
используя только лямбда-выражения?
    


Ответы

Ответ 1



Тоже самое, но без лишних запросов, только необходимые var counts = new { TotalUsers = _context.Users.Count(), TotalDeparments = _context.Depart.Count(), PeopleOver20YE = _context.Users.Count(c => c.YearOfBirth >= DateTime.Now.Year - 20), PeopleUnder20YE = _context.Users.Count(c => c.YearOfBirth < DateTime.Now.Year - 20) }; Если запросить счетчики в отдельные переменные, то можно использовать только два Users.Count, а третий вычислить через разность. int currentYear = DateTime.Now.Year;//получаем заранее, где-то в начале бизнес-действия int totalUsers = _context.Users.Count(); int totalDeparments = _context.Depart.Count(); int peopleOver20YE = _context.Users.Count(c => c.YearOfBirth >= currentYear - 20); var counts = new { TotalUsers = totalUsers, TotalDeparments = totalDeparments, PeopleOver20YE = peopleOver20YE, PeopleUnder20YE = totalUsers - peopleOver20YE };

Ответ 2



Во-первых, результат может сильно отличаться от используемой СУБД, вернее, от LINQ-провайдера для этой СУБД. Для SqlServer могут генерироваться одни запросы, для Oracle - другие, для каждой СУБД - свои. Во-вторых, Entity Framework (берём, конечно, последнюю версию: 6) и Entity Framework Core тоже сильно различаются и генерируют разные sql-запросы и по разному себя ведут. EF Core известен тем, что в некоторых случаях выполняет на клиенте те запросы, которые обычный EF выполнит на сервере. У меня нет возможности потестировать разные СУБД и версии EF. Возьму для опытов EF6 и SqlServer 2016. Создадим БД используя Code First: public class Departament { public int Id { get; set; } public string Name { get; set; } public virtual ICollection Users { get; set; } public Departament() { Users = new List(); } } public class User { public int Id { get; set; } public string Name { get; set; } public int YearOfBirth { get; set; } public int DepartamentId { get; set; } public virtual Departament Departament { get; set; } } public class MyContext : DbContext { public DbSet Users { get; set; } public DbSet Depart { get; set; } } Вставим данные: using (var _context = new MyContext()) { var d1 = new Departament { Name = "dep1" }; var d2 = new Departament { Name = "dep2" }; _context.Depart.AddRange(new Departament[] { d1, d2 }); var u1 = new User { Name = "nameA", YearOfBirth = 1991, Departament = d1 }; var u2 = new User { Name = "nameB", YearOfBirth = 1992, Departament = d2 }; var u3 = new User { Name = "nameC", YearOfBirth = 2001, Departament = d1 }; var u4 = new User { Name = "nameD", YearOfBirth = 2002, Departament = d2 }; _context.Users.AddRange(new User[] { u1, u2, u3, u4 }); _context.SaveChanges(); Console.WriteLine(_context.Users.Count()); Console.WriteLine(_context.Depart.Count()); } Для опытов этого достаточно. Код автора из вопроса using (var _context = new MyContext()) { _context.Database.Initialize(false); _context.Database.Log = Console.WriteLine; var counts = _context.Users.Select(user => new { TotalUsers = _context.Users.Count(), TotalDeparments = _context.Depart.Count(), PeopleOver20YE = _context.Users.Count(c => DateTime.Now.Year - c.YearOfBirth >= 20), PeopleUnder20YE = _context.Users.Count(c => DateTime.Now.Year - c.YearOfBirth < 20) }) .First(); Console.WriteLine(counts.TotalUsers + " " + counts.TotalDeparments + " " + counts.PeopleOver20YE + " " + counts.PeopleUnder20YE); } генерирует следующий sql: SELECT [Limit1].[C5] AS [C1], [Limit1].[C1] AS [C2], [Limit1].[C2] AS [C3], [Limit1].[C3] AS [C4], [Limit1].[C4] AS [C5] FROM ( SELECT TOP (1) [GroupBy1].[A1] AS [C1], [GroupBy2].[A1] AS [C2], [GroupBy3].[A1] AS [C3], [GroupBy4].[A1] AS [C4], 1 AS [C5] FROM [dbo].[Users] AS [Extent1] CROSS JOIN (SELECT COUNT(1) AS [A1] FROM [dbo].[Users] AS [Extent2] ) AS [GroupBy1] CROSS JOIN (SELECT COUNT(1) AS [A1] FROM [dbo].[Departaments] AS [Extent3] ) AS [GroupBy2] CROSS JOIN (SELECT COUNT(1) AS [A1] FROM [dbo].[Users] AS [Extent4] WHERE ((DATEPART (year, SysDateTime())) - [Extent4].[YearOfBirth]) >= 20 ) AS [GroupBy3] CROSS JOIN (SELECT COUNT(1) AS [A1] FROM [dbo].[Users] AS [Extent5] WHERE ((DATEPART (year, SysDateTime())) - [Extent5].[YearOfBirth]) < 20 ) AS [GroupBy4] ) AS [Limit1] Запрос с кучей соединений, но это один запрос. То есть будет выполнен один round trip. Возьмём теперь код из ответа rdorn: using (var _context = new MyContext()) { _context.Database.Initialize(false); _context.Database.Log = Console.WriteLine; var counts = new { TotalUsers = _context.Users.Count(), TotalDeparments = _context.Depart.Count(), PeopleOver20YE = _context.Users.Count(c => c.YearOfBirth >= DateTime.Now.Year - 20), PeopleUnder20YE = _context.Users.Count(c => c.YearOfBirth < DateTime.Now.Year - 20) }; Console.WriteLine(counts.TotalUsers + " " + counts.TotalDeparments + " " + counts.PeopleOver20YE + " " + counts.PeopleUnder20YE); } Он генерирует 4 простых запроса: SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Users] AS [Extent1] ) AS [GroupBy1] SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Departaments] AS [Extent1] ) AS [GroupBy1] SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Users] AS [Extent1] WHERE [Extent1].[YearOfBirth] >= ((DATEPART (year, SysDateTime())) - 20) ) AS [GroupBy1] SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[Users] AS [Extent1] WHERE [Extent1].[YearOfBirth] < ((DATEPART (year, SysDateTime())) - 20) ) AS [GroupBy1] Естественно, сам собой напрашивается второй его вариант, с вычислением одного количества на клиенте. Это будут три запроса. Однако, нам неизвестно, может у автора поле YearOfBirth является nullable? Что выгоднее: один сложный запрос или несколько простых? Думаю, любой спец по БД (а я таковым не являюсь) скажет, что это зависит от многих факторов. Если запросы идут через интернет, то лучше сократить их количество. Если БД расположена под боком, в локальной сети или даже на том же компе, то, вероятно, лучше избавиться от сложного запроса. Похоже, чисто linq-ом не сделать простой и эффективный запрос. Но можно сделать его вручную. using (var _context = new MyContext()) { _context.Database.Initialize(false); _context.Database.Log = Console.WriteLine; int year = DateTime.Now.Year - 20; string sql = @" declare @users int = (select count(Id) from [dbo].[Users]); declare @depts int = (select count(Id) from [dbo].[Departaments]); declare @over20YE int = (select count(Id) from [dbo].[Users] where YearOfBirth >= @year); declare @under20YE int = (select count(Id) from [dbo].[Users] where YearOfBirth < @year); select @users as TotalUsers, @depts as TotalDepartments, @over20YE as PeopleOver20YE, @under20YE as PeopleUnder20YE;"; var counts = _context.Database.SqlQuery(sql, new SqlParameter("year", year)).First(); Console.WriteLine(counts.TotalUsers + " " + counts.TotalDepartments + " " + counts.PeopleOver20YE + " " + counts.PeopleUnder20YE); } Модель для запроса: public class Counts { public int TotalUsers { get; set; } public int TotalDepartments { get; set; } public int PeopleOver20YE { get; set; } public int PeopleUnder20YE { get; set; } }

среда, 27 ноября 2019 г.

Медленный запрос count(*)


Я решил немного поиграться с относительно большими объемами данных. Создал просту
базу данных из двух таблиц и написал для нее программку, которая заполнила её данными. 
Первая вот такая: 

CREATE TABLE Cities
(
    Id int IDENTITY(1,1) NOT NULL PRIMARY KEY, 
    Name nvarchar(50) NULL
)


Она содержит 100 строк.
Вторая, главная: 

CREATE TABLE People
(
    Id int IDENTITY(1,1) NOT NULL,
    CityId int NOT NULL,
    Name nvarchar(50) NULL,
    LastName nvarchar(50) NULL,
    CONSTRAINT People_Id_Idx PRIMARY KEY CLUSTERED (Id ASC)
)

ALTER TABLE People  WITH CHECK ADD  CONSTRAINT fk_cities 
FOREIGN KEY(CityId) REFERENCES Cities (Id)


Она содержит 100 миллионов строк. Люди распределены по городам равномерно. 
Так вот вопрос. Когда я пишу очень простой запрос:

SELECT COUNT(*) -- COUNT(Id) даёт такой же результат
FROM People 


то он отрабатывает очень долго (около 100-130 секунд). Я конечно понимаю, что 10
миллионов записей - это довольно много, но всё-таки полторы-две минуты на такой запро
это очень много. Тем более, что необходимость узнать количество записей в таблице возникает довольно часто. Тем более, что сто миллионов записей - это совсем не предел. Ведь какие-нибудь банки ворочают таблицами с куда большим числом записей и столбцов. Как вообще они борются за производительность? 

Дополнительный данные: 


Версия sql-сервер: 
Microsoft SQL Server 2014 Enterprise Edition (64-bit)
План запроса: 


    


Ответы

Ответ 1



Ок, сначала стоит выяснить почему именно тормозит ваш запрос. Я вбил в таблицу 100 млн записей. Запустил count с измерением времени (с отключенным Show Actual Execution Plan - эта опция влияет на результаты измерений!): set statistics io on set statistics time on select count(*) from People Table 'People'. Scan count 9, logical reads 942693, physical reads 0, read-ahead reads 18, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. SQL Server Execution Times: CPU time = 8064 ms, elapsed time = 1169 ms. Запрос выполнился чуть больше чем за секунду. План выполнения, если его просмотреть, совпадает с вашим: Что плохо в таком плане выполнения, и почему запрос отработал быстро на моей машине, но медленно - на вашей? В вашей таблице есть кластерный индекс (это хорошо). Что такое кластерный индекс Это большое дерево поиска, состоящие из 8-килобайтных страниц (BTree, прошитое по уровням), в листьях которого хранятся данные записей. Как SQL сервер считает count(*) без условия? Он считает количество записей в Primar Key. Делает он это, проходя по нижнему уровню (листьям) дерева и подсчитывая количество записей в каждой странице. Каждое обращение к странице, которая уже находится в памяти - это logical read. Ка видите, SQL Server-у пришлось перебрать 942693*8 = примерно 7.5Gb данных ради подсчет количества строк. Это не сверхстрашно, но только в случае, когда данные таблицы уже оказались в памяти. В моем случае они лежали в памяти только потому, что SQL Server только что вставил данные в эту таблицу, и решил пока не сбрасывать кэш. Что происходит, если страницы, из которой нужно прочитать, в памяти не оказывается Например, когда у вас просто мало оперативной памяти, и база не влазит в ее целиком. Тогда logical read превращается в physical read - реальное чтение с диска. И вы получаете: Table 'People'. Scan count 9, logical reads 942693, physical reads 942693... SQL Server Execution Times: CPU time = ??? ms, elapsed time = 100000 ms (условно) Потому что SQL Server пытается читать с диска в 4/8/?? потоков, а даже линейное чтени 7.5Gb с диска по 100Mb/s дает вам задержку в 76.8 секунд (судя по скорости чтения - у вас HDD, а не SSD). Потери на нелинейность и одновременность (HDD это очень не любят) - и вот ваши 130 секунд в результате. Что можно сделать: Поставить больше памяти (ее никогда не бывает много!) Поставить SSD (значительно смягчит проблему) Заставить SQL Server читать меньше данных при подсчете count. Например, создать дополнительный некластерный индекс, в котором будут только ID: CREATE NONCLUSTERED INDEX [People_Id_Only] ON [dbo].[People] ( [Id] ASC ) ... и SQL Server начнет использовать его для подсчета количества строк: Table 'People'. Scan count 9, logical reads 125415, Всего прочитанных 979 мегабайт на 10 млн записей - не так уж и плохо! Использовать материализованные представления (Indexed Views) - оптимальный вариант. Хорошо расписан в ответе от i-one, я бы предложил вам выбрать именно это решение. Стоит отметить, что решения с индексами (3 и 4) не бесплатны с точки зрения поддержки Индексы требуют расходов на хранение (особенно видно в (3)) и на поддержание их в актульно состоянии (что чуть-чуть увеличивает время вставки записи). Решение на indexed view так же заметно усложняет план вставки каждой записи. Не удивляйтесь, если обнаружите что простая вставка insert into People (CityId, Name, LastName) values (1, 'test', 'one') начнет выполняться вот так: Процентам в плане слишком сильно верить нельзя, но стоит обращать на них внимание. Хорошо видно что решения (3) и (4) совсем не бесплатны. Кроме того, индексы обычно решают проблемы конкретных запросов. Например, созданны в (3) индекс будет неприменим при группировке по городам. А индекс с группировкой п городам может оказаться неэффективен при группировке по другому полю. Иногда наличие индекса может значительно ухудшить результат (например, в случае update на базе сложной выборке). Стоит всегда измерять результат до и после оптимизации, и не забывать, что индек может повлиять не только на тот запрос, который вы оптимизировали, но и на те запросы, что вы трогать не собирались.

Ответ 2



Если COUNT нужен в целях оценки, то, как верно отметили в комментариях, проще всего обратиться к DMV. Например к sys.dm_db_partition_stats: select sum(row_count) from sys.dm_db_partition_stats where object_id = object_id('People') and index_id in (0, 1); это даст приблизительное число строк в таблице (иногда точное, но точность не гарантируется). Если нужно точное число строк в таблице, то придётся честно выполнить запрос SELEC COUNT(*) FROM People. В ответе участника PashaPash хорошо описаны причины, по которы такой запрос может быть медленным и способы устранения. Мне также нравится ответ участника MaxU. Я бы использовал этот подход, только оформил бы это в виде материализованного представления: create view mvPeopleCityCnt with schemabinding as select CityId, Cnt = count_big(*) from dbo.People group by CityId; GO create unique clustered index mvPeopleCityCnt_Pk on mvPeopleCityCnt (CityId); GO Отмечу, что можно было бы материализовать запрос select count_big(*) from Peopl (не делая группировку по CityId). Учитывая, однако, что число городов относительно небольшое, материализация с группировкой по CityId может быть выгоднее, т.к. в этом случае в дополнение к запросу select count(*) from People; обращением к индексу mvPeopleCityCnt_Pk также могут быть удовлетворены, например, и такие запросы: select CityId, count(*) from People group by CityId; select distinct CityId from People; При их выполнении в случае наличия материализованного представления, SqlServer обратится к нему, а не к таблице People. Ниже сравнение производительности запроса SELECT COUNT(*) FROM People для четырёх разных подходов: clustered index (Id): Table 'People'. Scan count 5, logical reads 1290799 ... CPU time = 7333 ms, elapsed time = 2051 ms. nonclustered index (Id): Table 'People'. Scan count 5, logical reads 124188 ... CPU time = 6911 ms, elapsed time = 1840 ms. nonclustered columnstored index (Id): Table 'People'. Scan count 4, ..., lob logical reads 116054 ... CPU time = 999 ms, elapsed time = 252 ms. materialized view: Table 'mvPeopleCityCnt'. Scan count 1, logical reads 2 ... CPU time = 0 ms, elapsed time = 0 ms.

Ответ 3



Если надо быстро посмотреть количество строк в таблице, то можно воспользоватьс хранимкой sp_spaceused EXEC sys.sp_spaceused @objname = N'People' Второй столбец в результате - "rows" - и будет количеством строк в таблице на текущий момент.

Ответ 4



Я думаю в данном случае надо каким-то образом кешировать уже агрерированные данные. Один из вариантов создать таблицу: CREATE TABLE CityStats( CityId int, Population int, CONSTRAINT CityStats_CityId_Idx PRIMARY KEY CLUSTERED (CityId ASC) ); и обновлять (поддерживать) ее тригером на People...

Ответ 5



Узнавать число строк в таблице не имеет смысла - это число может устареть в следующи же момент. Если только вы не повесите блокировку на всю таблицу целиком. Если у вас такая необходимость возникает довольно часто - вы что-то делаете не так. Для диагностики можете взять какое-нибудь приближенное решение отсюда: https://habrahabr.ru/post/271797/ А если по какой-то причине нужны точные данные, в любой момент и быстро - можно воспользоваться индексированным представлением: create view PeopleCount with schemabinding as select count_big(*) as [count] from dbo.People go create unique clustered index PK_PeopleCount ON PeopleCount([count]) Использование: на младших редакциях SQL Server: select [count] from PeopleCount with (noexpand) на старших редакциях SQL Server можно сделать и так: select count(*) from People

Ответ 6



Количество строк можно узнать из системного представления dm_db_partition_stats по кластерному индексу: SELECT SUM(s.row_count) FROM sys.dm_db_partition_stats s WHERE s.[object_id] = OBJECT_ID('схема.таблица') AND s.index_id < 2; Также если неожиданно стал тормозить запрос типа: SELECT COUNT(*) FROM схема.таблица; , то возможно стоит обновить метаданные по этой таблице с помощью команды UPDATEUSAGE: DBCC UPDATEUSAGE(БД, 'схема.таблица') WITH COUNT_ROWS; Более детально обсуждалось здесь

Ответ 7



Попробуй сделать индекс по People.ID Id int IDENTITY(1,1) NOT NULL PRIMARY KEY, У таблицы People нет первичного ключа и поиск ведётся перебором всех записей. Детально проблему покажет explain select count(*) from people;

понедельник, 15 июля 2019 г.

Получить количество без повторений в одном из столбцов

Имеем таблицу менеджер - входящий номер:
call_time | manager | aon 10:01 | user1 | 7972207 10:14 | user2 | 8581041 10:20 | user3 | 3642345 11:00 | user1 | 8089142 11:01 | user2 | 7972207 11:13 | user2 | 0297188 11:22 | user1 | 8089142 11:34 | user3 | 3642345 11:50 | user4 | 3642345
Нужно получить количество уникальных звонков по менеджерам. Но уникальных звонков не у каждого менеджера, а уникальных звонков вообще. Уникальный звонок в данном случае - каждый уникальный aon. В таблице есть временная метка, поэтому кто первый принял звонок, тому и засчитываем. Например, 3642345 уже принял user3, поэтому для user4 его считать не нужно.
user1 2 user2 2 user3 1 user4 0


Ответ

Количество уникальных звонков с сортировкой по времени первой обработки номера:
SELECT `manager`, COUNT(*) AS `calls` FROM ( SELECT * FROM ( SELECT `manager`, `aon` FROM `calls` ORDER BY `call_time` ASC ) t1 GROUP BY `aon` ) t2 GROUP BY `manager`

вторник, 18 июня 2019 г.

Oracle count по условию

Всем доброго времени суток! Столкнулся с такой проблемой, есть запрос который возвращает
| SOT | KUT | K1 | K2 | YADRO | GR | |------|------|----|----|-------|-----| | 2055 | 1,31 | 0 | 3 | 2 | 101 | | 2055 | 1,31 | 0 | 3 | 2 | 101 | | 2055 | 1,31 | 0 | 3 | 2 | 103 | | 2055 | 1,31 | 1 | 0 | 1 | 100 | | 2055 | 1,31 | 0 | 3 | 3 | 102 | | 2055 | 1,31 | 0 | 3 | 3 | 102 |
Где столбцы K1 и K2 расчётные, K1 показывает сколько различных групп(GR) принадлежащих ядру(YADRO) 1 посещает сотрудник (SOT), K2 показывает сколько различных групп(GR) НЕ принадлежащих ядру(YADRO) 1 посещает сотрудник (SOT). Ядра содержат только уникальные группы, в двух разных ядрах группы не повторяются. Запрос
select sot, round(sum(time)over(partition by sot)/25920,2) as Кутил, case when yadro=2 then count(distinct gr)over(partition by sot,case when yadro=2 then 0 else 1 end) else 0 end as K1, case when yadro!=2 then count(distinct gr)over(partition by sot,case when yadro!=2 then 0 else 1 end) else 0 end as K2,
... from ...
Проблема в том что запрос возвращает значения с нулями а нужно чтобы было как то так
| SOT | KUT | K1 | K2 | YADRO | GR | |------ |------ |---- |---- |------- |----- | | 2055 | 1,31 | 1 | 3 | 2 | 101 | | 2055 | 1,31 | 1 | 3 | 2 | 101 | | 2055 | 1,31 | 1 | 3 | 2 | 103 | | 2055 | 1,31 | 1 | 3 | 1 | 100 | | 2055 | 1,31 | 1 | 3 | 3 | 102 | | 2055 | 1,31 | 1 | 3 | 3 | 102 |

Чтобы не было нулей а были общие цифры во всех столбцах, буду очень благодарен за помощь, сразу говорю обёртки предлагать только в крайнем случае, хотелось бы обойтись без лишних подзапросов и т.д.


Ответ

Не уверен , но стоит попробовать вот так :
count(distinct case when yadro=2 then gr else null end )over(partition by sot) as K1