分组并检查组是否包含

问题描述 投票:0回答:5

我的房子可以有“水槽”、“水龙头”和/或“厕所”固定装置。

我想找到哪些房屋至少拥有列表中的一个固定装置,但缺少其他固定装置并返回缺少的固定装置。

我将所有必需的灯具放在单独的列表中,而不是在数据库中。

例如,如果房子只有一个水槽,我想知道它缺少水龙头和马桶。

数据与结果

数据

房子 夹具
1 水槽
1 水龙头
1 厕所
2 水槽
2 水龙头
3 水槽

结果

房子 缺少固定装置
2 厕所
3 水龙头
3 厕所
sql oracle oracle-sqldeveloper
5个回答
1
投票

一个解决方案是创建所有房屋和所有固定装置的交叉连接表,然后将其与输入表左连接

enter co
WITH All_Fixtures as (
  SELECT distinct Fixture FROM Data ),
Houses as (
  SELECT distinct House FROM Data )
SELECT Houses.House , All_Fixtures.Fixture 
  FROM Houses CROSS JOIN All_Fixtures
  LEFT JOIN Data 
    ON Houses.House = Data.House AND All_Fixtures.Fixture = Data.Fixture
    WHERE Data.Fixture IS NULL 
    ORDER BY Houses.House;

即使在我们的桌子上添加了一些固定装置,它也能工作。

输入数据是由此查询创建的:

-- create 
CREATE TABLE Data (
  House NUMBER,
  Fixture VARCHAR2(10)
);

-- insert
INSERT INTO Data VALUES (1, 'Sink');
INSERT INTO Data VALUES (1, 'Faucet');
INSERT INTO Data VALUES (1, 'Toilet');
INSERT INTO Data VALUES (2, 'Sink');
INSERT INTO Data VALUES (2, 'Faucet');
INSERT INTO Data VALUES (3, 'Sink');

0
投票

使用连接查找至少具有一个固定装置的房屋,使用交叉连接生成所有组合,最后使用左连接(过滤不匹配)来查找丢失的固定装置:

with fixtures as (
  select 'Faucet' fixture from dual union
  select 'Sink' from dual union
  select 'Toilet' from dual
), house_ids_with_fixtures as (
  select distinct h.id
  from house_fixture h
  join fixtures f on f.fixture = h.fixture
)
select x.*
from (select * from house_ids_with_fixtures cross join fixtures) x
left join house_fixture h on h.id = x.id and h.fixture = x.fixture
where h.id is null
order by 1, 2

参见现场演示


0
投票

这是 SQL Server,但最后一个查询就是这个想法。

--SETUP TABLES
declare @types table (
typeId int,
name varchar(50))

declare @data table (
userId int,
typeId int)


--INSERT SAMPLE DATA
insert into @types 
values (1, 'Faucet'), (2, 'Toilet'),  (3, 'Sink')

insert into @data 
values (100, 1), (100, 2), (100, 3), (200, 1), (300, 2), (300, 3)

--GET RESULTS
select distinct d.userId, t.typeId, t.name
from @types t
cross join @data d
where not exists (
    select 1
    from @data din
    where din.userId = d.userId and din.typeId = t.typeId)
order by 1, 2

Results


0
投票

为了好玩,你也可以使用 MULTISET 来实现,只要你将 STRING_T 定义为 VARCHAR2 的表(choose_len_you_need):

with house_fixture(id, fixture) as (
  select 1, 'Sink' From Dual Union  
  select 1, 'Faucet' From Dual Union  
  select 1, 'Toilet' From Dual Union  
  select 2, 'Sink' From Dual Union  
  select 2, 'Faucet' From Dual Union  
  select 3, 'Sink' From Dual Union 
  select 4, 'WiFi' From Dual
)
, fixtures(fixture) as (
  select 'Faucet' from dual union
  select 'Sink' from dual union
  select 'Toilet' from dual
)
, sets(id, diff) as (
    select id, diff from (
        select 
            id,
            (
            cast(multiset (select fixture from fixtures) as string_t)
            multiset except
            cast(multiset (select fixture from house_fixture hf1 where hf1.id = hf.id) as string_t)
            ) as diff
        from 
            house_fixture hf
        group by id
    )
    where cardinality(diff) between 1 and (select count(*)-1 from fixtures) 
)
select s.id, t.column_value 
from sets s, table(s.diff) t
;

0
投票

编辑答案(问题编辑后)
使用 Case 表达式创建一个 cte,以获取具有 ID 的行中的所有 3 个灯具,并对缺少灯具的所有行进行 UNION...

甲骨文

Create Table table1      --  S a m p l e    D a t a :
     AS
      ( Select 1 as ID, 'Sink' as FIXTURE From Dual Union All 
        Select 1, 'Faucet' From Dual Union All 
        Select 1, 'Toilet' From Dual Union All 
        Select 2, 'Sink' From Dual Union All 
        Select 2, 'Faucet' From Dual Union All 
        Select 3, 'Sink' From Dual Union All
        Select 4, 'WiFi' From Dual
      );
WITH
  grid AS
      ( Select    t1.ID, 
                   Max(Case When t1.FIXTURE = 'Faucet' Then 1 Else 0 End) as FAUCET,
                   Max(Case When t1.FIXTURE = 'Sink' Then 1 Else 0 End) as SINK,
                   Max(Case When t1.FIXTURE = 'Toilet' Then 1 Else 0 End) as TOILET
        From      table1 t1
        Where     t1.FIXTURE IN('Faucet', 'Sink', 'Toilet')
        Group By  t1.ID
      )
--    S Q L :
Select ID, 'Faucet' as MISSING_FIXTURE From grid Where FAUCET = 0 Union All
Select ID, 'Sink' as MISSING_FIXTURE From grid Where SINK = 0 Union All
Select ID, 'Toilet' as MISSING_FIXTURE From grid Where TOILET = 0 
Order By ID
/*   R e s u l t :
ID  MISSING_FIXTURE
--  ---------------
 2  Toilet
 3  Faucet
 3  Toilet         */

在这里摆弄

在编辑问题之前先回答
一种选择是将您的一组固定装置与每个房屋 ID 的桌子固定装置列表进行比较。后者的创建取决于您使用的 RDBMS(Oracle 的 LISTAGG(),MySql 的 GROUP_CONCAT(),Postgres 的 STRING_AGG(),...)

...定义您的灯具组以与...进行比较

WITH      -- G r o u p    d e f i n i t i o n :
  fixture_group ( GROUP_ID, FIXTURE_GROUP) AS 
       ( Select 1, 'Faucet, Sink, Toilet' as FIXTURE From Dual )

...比较列表并从组中删除现有灯具、逗号字符和前导/尾随空格字符。如果留下 null 则不会丢失任何内容 ( '-' ) ...

--    M a i n    S Q L :
Select    Distinct t1.ID, 
          TRIM( REPLACE(
          COALESCE( REPLACE( fg.FIXTURE_GROUP, 
                               LISTAGG(t1.FIXTURE, ', ') WITHIN GROUP 
                               (Order By t1.FIXTURE) Over(Partition By t1.ID), 
                            ''), 
                  '-'),
         ',', '') ) as MISSING_FIXTURE_LIST
From      table1 t1
Inner Join fixture_group fg ON( INSTR(fg.FIXTURE_GROUP, t1.FIXTURE) > 0 )
Order By  t1.ID;
/*    R e s u l t : 
ID  MISSING_FIXTURE_LIST
--  --------------------
 1  -
 2  Toilet
 3  Faucet  Toilet        */
© www.soinside.com 2019 - 2024. All rights reserved.