Как найти крайние пустые группы товаров?
Имеются 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 шт):
Вам надо сделать выборку с 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)
А вот отфильтровать группы без подргупп - это уже запросом к бд не получится, это логика реализуется в приложении (это парсинг по значению поля, относительно распарсеных значений других полей). бд по значению полей может только фильтровать.
Получился такой запрос, т.к. запарсить ответы в приложении не получится, нужно сразу в бд всё делать. И получилось даже путь до группы указать для пользователя
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)