2 таблицы 1 результат, хитрый SQL - PullRequest
0 голосов
/ 29 мая 2020

После многих лет сокрытия stackoverflow у меня наконец-то возникла необходимость обратиться к вам, гуру разработки, в поисках небольшой помощи.

Контекст:

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

Мои таблицы выглядят так:

CREATE TABLE USERS 
(
     SID VARCHAR(255), 
     SYSID VARCHAR(5), 
     MANDT VARCHAR(3), 
     BNAME VARCHAR(255), 
     GLTGV DATE, 
     GLTGB DATE, 
     USTYP VARCHAR(2), 
     LOCNT VARCHAR(3), 
     UFLAG VARCHAR(3), 
     TRDAT DATE, 
     LTIME VARCHAR(255), 
     CLASS VARCHAR(255), 
     PWDCHGDATE DATE, 
     PROFILE VARCHAR(255)
)

CREATE TABLE ROLES 
(
     SID VARCHAR(255), 
     SYSID VARCHAR(5), 
     MANDT VARCHAR(3), 
     UNAME VARCHAR(255), 
     AGR_NAME VARCHAR(255)
)

Не беспокойтесь об определениях, я просто их «пустышка» для простоты.

Один пользователь может не иметь ни одной или многих ролей. Как вы ie их вместе?

Хорошо SYSID, MANDT и BNAME из таблицы USERS должны соответствовать содержимому таблицы ROLES, столбцов SYSID, MANDT и UNAME (все того же типа)

Итак, я создал этот запрос

SELECT DISTINCT
    t1.SID, 
    t1.SYSID, 
    t1.MANDT, 
    t1.BNAME, 
    t1.GLTGV, 
    t1.GLTGB, 
    t1.USTYP, 
    t1.LOCNT, 
    t1.UFLAG, 
    t1.TRDAT, 
    t1.LTIME, 
    t1.CLASS, 
    t1.PWDCHGDATE, 
    t1.PROFILE
FROM 
    USERS AS t1, 
    ROLES AS t2 
WHERE 
    t1.SYSID = t2.SYSID AND 
    t1.MANDT = t2.MANDT AND 
    t1.BNAME = t2.UNAME AND
    (t2.AGR_NAME = "ZTEST_ROLE")

, который работает хорошо, пока я запрашиваю только одну РОЛЬ.

Чтобы запросить более одной роль, делаю

SELECT DISTINCT
    t1.SID, 
    t1.SYSID, 
    t1.MANDT, 
    t1.BNAME, 
    t1.GLTGV, 
    t1.GLTGB, 
    t1.USTYP, 
    t1.LOCNT, 
    t1.UFLAG, 
    t1.TRDAT, 
    t1.LTIME, 
    t1.CLASS, 
    t1.PWDCHGDATE, 
    t1.PROFILE
FROM 
    USERS AS t1, 
    ROLES AS t2 
WHERE 
    t1.SYSID = t2.SYSID AND 
    t1.MANDT = t2.MANDT AND 
    t1.BNAME = t2.UNAME AND
    (t2.AGR_NAME = "ZTEST_ROLE" OR t2.AGR_NAME = "ZTEST_ROLE2")

Тоже работает. Вы могли бы использовать этот запрос, если хотите получить информацию от всех пользователей, которые имеют ZTEST_ROLE или ZTEST_ROLE2 в качестве ролей.

Теперь вот моя проблема: я не могу заставить ее работать, если я хочу выбрать всех пользователей с обеими ролями ZTEST_ROLE и ZTEST_ROLE2 . мой запрос всегда возвращает 0 строк

Вот запрос, который я использую

SELECT DISTINCT
    t1.SID, 
    t1.SYSID, 
    t1.MANDT, 
    t1.BNAME, 
    t1.GLTGV, 
    t1.GLTGB, 
    t1.USTYP, 
    t1.LOCNT, 
    t1.UFLAG, 
    t1.TRDAT, 
    t1.LTIME, 
    t1.CLASS, 
    t1.PWDCHGDATE, 
    t1.PROFILE
FROM 
    USERS AS t1, 
    ROLES AS t2 
WHERE 
    t1.SYSID = t2.SYSID AND 
    t1.MANDT = t2.MANDT AND 
    t1.BNAME = t2.UNAME AND
    (t2.AGR_NAME = "ZTEST_ROLE" AND t2.AGR_NAME = "ZTEST_ROLE")

Я играл с этим некоторое время, и я не вижу, где моя ошибка

Для удобства и на случай, если вы захотите попробовать это в Интернете, я использовал https://sqliteonline.com/

Я ценю вашу помощь и приношу свои извинения за длинный пост

Ответы [ 3 ]

0 голосов
/ 30 мая 2020

Я думаю, что такое решение - это то, что вы ищете ... Я могу сделать это разными способами, но я думаю, вы уловили идею.

SELECT DISTINCT 
    t1.SID, 
    t1.SYSID, 
    t1.MANDT, 
    t1.BNAME, 
    t1.GLTGV, 
    t1.GLTGB, 
    t1.USTYP, 
    t1.LOCNT, 
    t1.UFLAG, 
    t1.TRDAT, 
    t1.LTIME, 
    t1.CLASS, 
    t1.PWDCHGDATE, 
    t1.PROFILE
FROM (
    SELECT DISTINCT
        t1.SID, 
        t1.SYSID, 
        t1.MANDT, 
        t1.BNAME, 
        t1.GLTGV, 
        t1.GLTGB, 
        t1.USTYP, 
        t1.LOCNT, 
        t1.UFLAG, 
        t1.TRDAT, 
        t1.LTIME, 
        t1.CLASS, 
        t1.PWDCHGDATE, 
        t1.PROFILE,
        case when t2.AGR_NAME = "ZTEST_ROLE" then 1 else 0 end as t2t1,
        case when t2.AGR_NAME = "ZTEST_ROLE2" then 1 else 0 end as t2t2
    FROM 
        USERS AS t1, 
        ROLES AS t2 
    WHERE 
        t1.SYSID = t2.SYSID AND 
        t1.MANDT = t2.MANDT AND 
        t1.BNAME = t2.UNAME AND
        (t2.AGR_NAME = "ZTEST_ROLE" or t2.AGR_NAME = "ZTEST_ROLE")
    ) as tmp
    WHERE t2t1 = 1 and t2t2 = 1
0 голосов
/ 30 мая 2020

Вы можете использовать два exists подзапроса, по одному для каждой роли:

select u.*
from users u
where
    exists (
        select 1
        from roles r 
        where 
            r.ysid = u.sysid
            and r.mandt = u.mandt
            and r.uname = r.uname
            and r.agr_name = 'ZTEST_ROLE'
    ) 
    and exists (
        select 1
        from roles r 
        where 
            r.ysid = u.sysid
            and r.mandt = u.mandt
            and r.uname = u.bname
            and r.agr_name = 'ZTEST_ROLE2'
    )

Также это кажется довольно запутанным, это должно быть эффективным вариантом, особенно если у вас есть индекс на roles(sysid, mandt, uname, agr_name).

Другое решение - фильтрация с помощью агрегированного запроса:

select u.*
from users u
where
    exists (
        select count(*)
        from roles r 
        where 
            r.ysid = u.sysid
            and r.mandt = u.mandt
            and r.uname = r.uname
            and r.agr_name in ('ZTEST_ROLE', 'ZTEST_ROLE2')
    )  = 2

Положительная сторона второго подхода заключается в том, что его легче расширить для проверки сразу нескольких ролей: вам просто нужно разверните список справа от оператора in и соответственно увеличьте целевое количество.

0 голосов
/ 29 мая 2020

Вы не можете иметь оба одновременно

(t2.AGR_NAME = "ZTEST_ROLE" and t2.AGR_NAME = "ZTEST_ROLE2")

здесь вы говорите, что t2.AGR_NAME = "ZTEST_ROLE" должно быть истинным, а также t2.AGR_NAME = "ZTEST_ROLE2" должно быть истинным в той же строке.

вы могли бы сделать

(t2.AGR_NAME = "ZTEST_ROLE" and (select AGR_NAME from ROLES where uname = t1.bname and agr_name = "ZTEST_ROLE2") = "ZTEST_ROLE2"))

, но я сомневаюсь, что это лучший способ сделать это. вроде хаки

Вот ваш пример

https://dbfiddle.uk/?rdbms=sqlite_3.27&fiddle=18052895bcd6bea155ef01c957a06ff7

...