Как найти крайние пустые группы товаров?

Имеются 2 таблицы, одна таблица goods с товарами и таблица GoodsGroups, чтобы не загружать тут все текстам сокращу до нужного. в таблице goods есть 2 столбца, это ID и GroupID. В таблице GoodsGroups есть 2 значения, это ID и Code. Всё это для торговой программы, Code отвечает за код группы и формируется по правилу AAA - самое первое значение первой группы, далее AAB с смещением последней буквы - это следующая группа, и т.д. AAC, AAD, AAE. Также мы можем создать подгруппу товаров, и при её создании к коду главной группы прибавляются 3 буквы A и код подгруппы получается AAAAAA. При создании 2 подгруппы в главной AAA получается код AAAAAB, т.е. с тем же смещением и т.д. AAAAAC, AAAAAD И так подгруппы могут формироваться сколько им нужно и code всегда будем иметь по 3,6,9,12 и т.д. символов. Так вот, таблицы связаны как goods.groupid = GoodsGroups.ID В базе у клиента подгрупп более 18тыс., что плохо сказывается на работе в целом. Если пролистывать группы в самой программе, то видно что некоторые из них пустые. Структура построена так что в главных группах как правило нет товаров, они уже распределены по подгруппам. И соответственно в главной группе без товаров есть 2 подгруппы опять без товаров у которых есть 6 подгрупп. Одна из них без товаров, остальные с товарами. По коду это буедет выглядеть так гл. группа AAA, две подгруппы aaaAAA и aaaAAB, у них уже aaaAAAaaa, aaaAAAaab, aaaAAAaac и aaaAABaaa, aaaAABaab, aaaAABaac. и например первая (aaaAAAaaa) без товаров, т.е. по запросу select id from goods where groupid in (select id from GoodsGroups = 'AAAAAAAAA') я получу 0 результатов. Но уже в соседней группе товар есть, а в группе родителе товаров нет. Каким запросом вообще можно увидеть список групп без товаров и у которых больше нет "детей"? При этом соответственно игнорируя главные групп, т.к. у них есть подгруппы. Пример заполненных таблиц


Ответы (2 шт):

Автор решения: Mira

Вам надо сделать выборку с join

SELECT GoodsGroups.*
FROM GoodsGroups LEFT JOIN goods
ON goods.groupid = GoodsGroups.ID
WHERE goods.ID IS NULL

Вариант c подзапросом будет работать несколько медленнее:

SELECT
   *
FROM
   GoodsGroups
WHERE
   NOT EXISTS (SELECT * FROM goods
     WHERE
         goods.groupid = GoodsGroups.ID)

А вот отфильтровать группы без подргупп - это уже запросом к бд не получится, это логика реализуется в приложении (это парсинг по значению поля, относительно распарсеных значений других полей). бд по значению полей может только фильтровать.

→ Ссылка
Автор решения: Илья Oktane

Получился такой запрос, т.к. запарсить ответы в приложении не получится, нужно сразу в бд всё делать. И получилось даже путь до группы указать для пользователя

IF OBJECT_ID('dbo.codes', 'U') IS NOT NULL  
DROP TABLE dbo.codes;  
IF OBJECT_ID('dbo.truecodes', 'U') IS NOT NULL  
DROP TABLE dbo.truecodes;  
select code 
into codes 
from GoodsGroups  
where id not in (select groupid from Goods) 
create table truecodes 
(truecode nvarchar(255), code1 nvarchar(255), code2 nvarchar(255), code3 nvarchar(255), code4 nvarchar(255), code5 nvarchar(255), code6 nvarchar(255)) 
go 
DECLARE @supercode nvarchar(255); 
DECLARE @code1 nvarchar(255); 
DECLARE @code2 nvarchar(255); 
DECLARE @code3 nvarchar(255); 
DECLARE @code4 nvarchar(255); 
DECLARE @code5 nvarchar(255); 
DECLARE @code6 nvarchar(255); 
DECLARE @counter int; 
declare @counter_count int; 
set @counter_count = (select count(code) from codes) 
while @counter_count > 0  
begin 
SET @supercode = (select top 1 Code from codes) 
set @counter = (select count(code) from GoodsGroups where code like @supercode+'%') 
if @counter > 1 exec ('') 
else  
begin 
if LEN(@supercode) = 18 
begin 
set @code1 = substring (@supercode,1,3)  
set @code2 = substring (@supercode,1,6) 
set @code3 = substring (@supercode,1,9) 
set @code4 = substring (@supercode,1,12) 
set @code5 = substring (@supercode,1,15) 
set @code6 = substring (@supercode,1,18) 
insert into [truecodes] values(@supercode, @code1, @code2, @code3, @code4, @code5, @code6); 
end 
if LEN(@supercode) = 15 
begin 
set @code1 = substring (@supercode,1,3)  
set @code2 = substring (@supercode,1,6) 
set @code3 = substring (@supercode,1,9) 
set @code4 = substring (@supercode,1,12) 
set @code5 = substring (@supercode,1,15) 
insert into [truecodes] values(@supercode, @code1, @code2, @code3, @code4, @code5, NULL); 
end 
if LEN(@supercode) = 12 
begin 
set @code1 = substring (@supercode,1,3)  
set @code2 = substring (@supercode,1,6) 
set @code3 = substring (@supercode,1,9) 
set @code4 = substring (@supercode,1,12) 
insert into [truecodes] values(@supercode, @code1, @code2, @code3, @code4, NULL, NULL); 
end
if LEN(@supercode) = 9  
begin 
set @code1 = substring (@supercode,1,3)  
set @code2 = substring (@supercode,1,6) 
set @code3 = substring (@supercode,1,9) 
insert into [truecodes] values(@supercode, @code1, @code2, @code3, NULL, NULL, NULL); 
end 
if LEN(@supercode) = 6  
begin 
set @code1 = substring (@supercode,1,3)  
set @code2 = substring (@supercode,1,6) 
insert into [truecodes] values(@supercode, @code1, @code2, NULL, NULL, NULL, NULL); 
end 
if LEN(@supercode) = 3  
begin 
set @code1 = substring (@supercode,1,3)  
insert into [truecodes] values(@supercode, @code1, NULL, NULL, NULL, NULL, NULL); 
end 
end 
delete from codes where code = @supercode 
set @counter_count = @counter_count - 1 
end 
select truecodes.truecode, 
GoodsGroups.Name as [Краяняя], 
[Имя главной] = (select name from goodsgroups where code = code1), 
[Имя второй] = (select name from goodsgroups where code = code2), 
[Имя третьей] = (select name from goodsgroups where code = code3), 
[Имя четвертой] = (select name from goodsgroups where code = code4), 
[Имя пятой] = (select name from goodsgroups where code = code5), 
[Имя шестой] = (select name from goodsgroups where code = code6) 
from truecodes join GoodsGroups on dbo.truecodes.truecode = GoodsGroups.Code 
delete from GoodsGroups where code in (select truecode from truecodes)
→ Ссылка