Many to one where the one can be one of two different fields - postgresql

I've had a look at similar posts on one to many and many to one relationships but I just can't find what I'm looking for. I have 2 tables. The first (vm) contains details of virtual machines. The vms are clustered in pairs and the second table (cluster) has details of the cluster. The set up is as follows:
vm table cluster table
vmId clusterId
ipaddress primaryId
macaddress secondaryId
The relationship is that the vm.vmId can be a match to either cluster.primaryId or cluster.secondaryId. With only an ipaddress I want to be able to get the full cluster details as follows:
clusterid vmId ipaddress macaddress
c1 v1 10.0.10.10 AA:BB:CC:DD:10:11
C1 v2 10.0.10.11 AA:BB:CC:DD:10:12
and that's my problem. With just the ipaddress I can find the vmId and with that I can get the cluster, but I can't then work out how to use the non matching id in the cluster table to get the other vm details. I've tried variables and unions etc., but I must be doing something wrong because I keep coming up blank. I'm looking for a single select statement that will provide the required result. Can anyone suggest a solution please?

First find the cluster(s) with given IP:
select *
from cluster
where exists (
select 1
from vm
where
(vm.vmId = cluster.primaryId or vm.vmId = cluster.secondaryId) and
wm.ipaddress = '127.0.0.1');
Then join this with vm table in the same way:
with clusters as (
select *
from cluster
where exists (
select 1
from vm
where
(vm.vmId = cluster.primaryId or vm.vmId = cluster.secondaryId) and
wm.ipaddress = '127.0.0.1'))
select *
from clusters join vm on (vm.vmId = clusters.primaryId or vm.vmId = clusters.secondaryId);

This is probably what you are looking for, but realize that the vmIds will appear twice, once under the primary cluster, and another under the secondary cluster.
SELECT clusterid, vmId, ipaddress, macaddress
FROM vm
JOIN cluster
ON vm.vmId = cluster.primaryId
UNION ALL
SELECT clusterid, vmId, ipaddress, macaddress
FROM vm
JOIN cluster
ON vm.vmId = cluster.secondaryId
The above can be reduced to:
SELECT clusterid, vmId, ipaddress, macaddress
FROM vm
JOIN cluster
ON (vm.vmId = cluster.primaryId OR vm.vmId = cluster.secondaryId)
If you want to filter the result:
SELECT clusterid, vmId, ipaddress, macaddress
FROM vm
JOIN cluster
ON (vm.vmId = cluster.primaryId OR vm.vmId = cluster.secondaryId)
WHERE ipaddress = '10.0.10.10'

With your help I did finally manage to get the answer I was looking for. With a little modification of your suggestions I finally came up with this.
with clusters as (
select clusterId
from cluster
where exists (
select 1
from vm
where
(vm.vmId = cluster.primaryId or vm.vmId = cluster.secondaryId) and
wm.ipaddress = '10.0.10.10'))
select clusterId, vmId, ipaddress, macaddress
from vm
join cluster on (vm.vmId = cluster.primaryId or vm.vmId = cluster.secondaryId)
where cluster.clusterId = (select * from clusters)
Produced exactly the result I was looking for. Thanks everyone for your assistence.

Related

How to read / list security labels on columns in postgreSQL

I've set up PostgreSQL Anonymizer on my database with security labels and everything works fine.
I'm trying to regularly ceck if there is missing security labels on the columns of my database to telle the developers to add them in the next release but I can't fin a way to read the security labels.
Can anyone know how to do this ?
EDIT on 10/11/2022
Thanks to #Shiva, I've end up doing this query :
select cl."oid", col.ordinal_position, col.table_schema, col.table_name, col.column_name
FROM information_schema.columns col
join pg_catalog.pg_class cl on cl.relname = col.table_name
WHERE col.table_schema = 'XXXX'
and not exists (select objoid FROM pg_seclabel where provider = 'anon' and objsubid = col.ordinal_position and objoid = cl."oid");
You have to query pg_seclabel catalog to get list of security labels.
SELECT objsubid, provider, label FROM pg_seclabel WHERE objoid::regclass = 'mytable'::regclass
objsubid is the column number whose corresponding column name can be found by querying information_schema.columns catalog.
SELECT column_name FROM information_schema.columns WHERE table_name = 'mytable' AND ordinal_position = <column_number>
You can combine the above two queries to find columns that do not have the required security labels.

T-SQL Question for Getting One Customer Type When There Can be More Than One Value

We have an organization that can have more than one customer type basically. However, what a user wants to see is either the partner or direct type (customer type is either Direct, Partner1, Partner2, or Partner3 but can be direct plus a partner value but only can be one of the partner values). So if a customer is both (ex: Direct and Partner1) they just want the type that is a partner (ex: Partner1). So I tried splitting out partners only into one temp table from a few tables joining together different org data. I have the same query without any limit pulling into a different temp table. Then I calculate count and put that into a temp table. Then I tried gathering data from all the temp tables. That is where I run into trouble and lose some of the customers where the type is direct (I have a image link below for a directcustomer and a customer who is both). I have been out of SQL for a bit so this one is throwing me...I figure the issue is the fact that I have a case statement referencing a table that a direct customer will not exist in (#WLPO). However I am not sure how to achieve pulling in these customers while also only selecting which partner type it is for a customer that has a partner and is also direct. FYI using MSSMS for querying.
If OBJECT_ID('tempdb..#WLPO') IS NOT NULL
DROP TABLE #WLPO
IF OBJECT_ID('tempdb..#org') IS NOT NULL
DROP TABLE #org
IF OBJECT_ID('tempdb..#OrgCount') IS NOT NULL
DROP TABLE #OrgCount
IF OBJECT_ID('tempdb..#cc') IS NOT NULL
DROP TABLE #cc
Select
o.OrganizationID,
o.OrganizationName,
os.WhiteLabelPartnerID,
s.StateName
INTO #WLPO
from [Org].[Organizations] o
join [Org].[OrganizationStates] os on o.OrganizationID=os.OrganizationID --and os.WhiteLabelPartnerID = 1
join [Lookup].[States] s on os.StateID = s.StateID
join [Org].[PaymentOnFile] pof on pof.OrganizationID=o.OrganizationID
where os.WhiteLabelPartnerID in (2,3,4)
and os.StateID in (1, 2, 3)
and o.OrganizationID = 7613
select * from #WLPO
Select
o.OrganizationID,
o.OrganizationName,
os.WhiteLabelPartnerID,
s.StateName
INTO #org
from [Org].[Organizations] o
join [Org].[OrganizationStates] os on o.OrganizationID=os.OrganizationID --and os.WhiteLabelPartnerID = 1
join [Lookup].[States] s on os.StateID = s.StateID
join [Org].[PaymentOnFile] pof on pof.OrganizationID=o.OrganizationID
where 1=1--os.WhiteLabelPartnerID = 1
and os.StateID in (1, 2, 3)
and o.OrganizationID = 7613
select * from #org
Select
OrganizationID,
count(OrganizationID) AS CountOrgTypes
INTO #OrgCount
from #org
where OrganizationID = 7613
group by OrganizationID
select * from #OrgCount
Select distinct
ct.OrganizationID,
ok.OrganizationName,
ct.CountOrgTypes,
case when ct.CountOrgTypes = 2 then wlp.WhiteLabelPartnerID
when ct.CountOrgTypes = 1 then ok.WhiteLabelPartnerID
END AS CustomerTypeCode,
case when ct.CountOrgTypes = 2 then wlp.StateName
when ct.CountOrgTypes = 1 then ok.StateName END As OrgState
INTO #cc
from #org ok
left join #WLPO wlp on wlp.OrganizationID=ok.OrganizationID
join #OrgCount ct on wlp.OrganizationID=ct.OrganizationID
select * from #cc
Select
OrganizationID,
OrganizationName,
CountOrgTypes,
case when CustomerTypeCode = 1 then 'Direct'
when CustomerTypeCode = 2 then 'Partner1'
when CustomerTypeCode = 3 then 'Partner2'
when CustomerTypeCode = 4 then 'Partner3' ELSE Null END AS CustomerType,
OrgState
from #cc
order by OrganizationName asc
DirectCustomer
CustomerwithBoth

PostsgreSQL. How to return the number of records in the users table who have name Latifah, Elizabeth or Diana?

I have this :
SELECT count(*) FROM users
WHERE name = 'Latifah'
AND name = 'Elizabeth'
AND name = 'Diana';
It returns zero. What's wrong? Is there a way to make it shorter?
you can "aggregate" ORs to IN operator:
SELECT count(*)
FROM users
WHERE name IN ('Latifah','Elizabeth','Diana');
This is also worked:
SELECT count(*) FROM users
WHERE name = 'Latifah'
or name = 'Elizabeth'
or name= 'Diana';

Keycloak - Get all Users mapped to roles

I know keycloak has exposed below api,
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<version>2.0.0.Final</version>
</dependency>
With complete documentation here. I cannot find the required api here to fetch all users with specific role mapped to them.
Problem Statement - I need to pick all users from keycloak server who have a specific role. I need to send email to all users with role mapped to them.
Based on the documentation it appears to be this API:
GET /{realm}/clients/{id}/roles/{role-name}/users
It is there for a while. In this older version however it was not possible to get more than 100 users this way. It was fixed later and pagination possibility was added.
There is an outstanding feature request asking for this function via the API.
In the meantime if your requirement is once-off you could obtain the user names (or email addresses) by interrogating the database joining KEYCLOAK_ROLE to USER_ROLE_MAPPING to USER_ENTITY
Something like:
SELECT username
FROM keycloak_role kr
JOIN user_role_mapping rm ON kr.id = rm.role_id
JOIN user_entity ue ON rm.user_id = ue.id
WHERE kr.name = 'your_role_name';
This should be now possible with the updated rest endpoint.
Set<UserRepresentation> usersOfRole = realmResource.roles().get(roleName).getRoleUserMembers();
Here is another interesting query, which would also display other useful fields.
SELECT kr_role.REALM_ID 'Realm', cl.CLIENT_ID 'Realm Client',
kr_role.NAME 'Role Name',
kr_role.DESCRIPTION 'Role Description',
user_ent.USERNAME 'Domain ID', user_ent.EMAIL 'Email'
FROM keycloak_role kr_role, user_role_mapping role_map,
user_entity user_ent, client cl
WHERE role_map.USER_ID = user_ent.ID
AND kr_role.ID = role_map.ROLE_ID
AND kr_role.CLIENT = cl.ID
AND cl.REALM_ID = '<realm_name>'
AND cl.CLIENT_ID = '<client_name>'
ORDER BY 1, 2, 3;
If anyone is still searching for a Postgres Query to find information regarding users/roles/groups in keycloak database, I came up with this one lately.
It uses two CTEs to have the groups and roles straight (recursing for groups in groups, because they can nest in arbitrary depth and fetching composite roles with their parents) and a simple UNION for group and direct assignments.
Please note the WHERE clause, where you can limit the realm and different aspects. You can search for
all roles from a specific user (just matching username)
all users, that have a particular role assigned (matching role_name)
everything coming from a specific group (I sometimes use it without the username column in the projection to just see, what roles a group has. Please note the prefix in the group column)
-- flat out GROUPS in GROUPS
WITH RECURSIVE groups AS (
SELECT
g.id,
g.id AS parent_group,
g.name,
g.name AS parent_name,
g.realm_id,
1 AS iter
FROM
keycloak_group g
UNION
SELECT
groups.id,
parents.parent_group,
groups.name,
parents.name,
groups.realm_id,
groups.iter + 1
FROM
groups
INNER JOIN keycloak_group parents ON groups.parent_group = parents.id
),
-- Collect roles and composite roles
roles AS (
SELECT
r.id,
r.name AS role_name,
null AS base_role,
c.client_id
FROM
keycloak_role r
LEFT JOIN client c ON r.client = c.id
UNION
SELECT
r.id,
r2.name,
r.name,
c.client_id
FROM
keycloak_role r
JOIN composite_role cr ON r.id = cr.composite
JOIN keycloak_role r2 ON r2.id = cr.child_role
LEFT JOIN client c ON r.client = c.id
)
SELECT DISTINCT
username,
role_name,
base_role, -- for composite roles
client_id,
source,
realm_name
FROM
(
-- Roles from Groups
SELECT
ue.username,
roles.role_name,
roles.base_role,
roles.client_id,
ue.realm_id,
'group ' || g.name AS source,
realm.name AS realm_name
FROM
user_entity ue
JOIN realm ON ue.realm_id = realm.id
JOIN user_group_membership ugm ON ue.id = ugm.user_id
JOIN groups g ON g.id = ugm.group_id
JOIN group_role_mapping grm ON g.parent_group = grm.group_id
JOIN roles roles ON roles.id = grm.role_id
UNION
-- direct role assignments on User
SELECT
ue.username,
roles.role_name,
roles.base_role,
roles.client_id,
ue.realm_id,
'direct',
realm.name
FROM
user_entity ue
JOIN realm ON ue.realm_id = realm.id
JOIN user_role_mapping urm ON ue.id = urm.user_id
JOIN roles roles ON roles.id = urm.role_id
) AS a
WHERE
realm_name = 'realm_name'
AND (
-- username = 'username'
role_name IN ('roleName')
-- source = 'group GROUPNAME'
)
ORDER BY
username,
role_name
;
This query works from keycloak 9 to 16.1.1 (the last jboss/keycloak version I got from docker hub).
SELECT username,
kr.NAME,
kr.REALM_ID
FROM KEYCLOAK_ROLE kr
JOIN USER_ROLE_MAPPING rm ON kr.id = rm.role_id
JOIN USER_ENTITY ue ON rm.user_id = ue.id
ORDER BY USERNAME,
NAME,
REALM_ID;

SELECT attribute with MIN value from set of attribute instances that are part of specific row?

I know my question is probably very vague and hard to understand at first glance, and i've sat 30 minutes thinking about a proper title. However my database knowledge is very limited so I have a hard time formulating myself properly yet.
It is part of a school assignment I'm currently doing, where the following is what I'm trying to achieve:
and an ER diagram I made of the system:
What I'm trying to accomplish is, selecting the quantity of the component within the computer_system that has the lowest quantity (current stock) so that I in the dataset I am printing out, am able to state exactly how many of each computer_system the store is able to sell, based on the lowest current quantity of any component the computer_system consists of.
This is the query that i am currently working with to accomplish it, but I've had multiple problems with quantity being ambiguous and other errors every time I try to make a fix. I have consulted a dozen of friends from class, but without luck.
SELECT
computer_system.NAME,
cpu.name as cpu,
gpu.name as gpu,
board.name as mainboard,
pccase.name as pc_case,
ram.name as ram,
component.quantity as qty,
(cpu.price *1.3+ board.price*1.3 + pccase.price*1.3 + ram.price*1.3 + gpu.price*1.3) as computer_system_price
FROM computer_system, component
join component cpu on cpu.id = computer_system.cpu
join component gpu on gpu.id = computer_system.gpu
join component board on board.id = computer_system.mainboard
join component pccase on pccase.id = computer_system.pc_case
join component ram on ram.id = computer_system.ram
JOIN component qty ON qty.quantity = (SELECT MIN(component.quantity) FROM component WHERE component.id IN
(computer_system.pc_case,
computer_system.mainboard,
computer_system.cpu,
computer_system.gpu,
computer_system.ram))
The following code fixed it for me:
SELECT computer_system.name AS "System name",
cpu.name AS "CPU",
gpu.name AS "GPU",
pc_case.name AS "Case",
mainboard.name AS "Mainboard",
ram.name AS "RAM",
FLOOR((cpu.price + mainboard.price + pc_case.price + ram.price + coalesce(gpu.price, 0))*1.3/100)*100+99 AS "System price",
SYSTEM.maxamount
FROM computer_system
join (SELECT name,
id,
price
FROM component) AS cpu
ON cpu.id = computer_system.cpu
left join (SELECT name,
id,
price
FROM component) AS gpu
ON coalesce(gpu.id,0) = computer_system.gpu
join (SELECT name,
id,
price
FROM component) AS pc_case
ON pc_case.id = computer_system.pc_case
join (SELECT name,
id,
price
FROM component) AS mainboard
ON mainboard.id = computer_system.mainboard
join (SELECT name,
id,
price
FROM component) AS ram
ON ram.id = computer_system.ram
join (SELECT computer_system.name,
Min(component.quantity) AS maxamount
FROM computer_system,
component
WHERE computer_system.cpu = component.id
OR computer_system.gpu = component.id
OR computer_system.mainboard = component.id
OR computer_system.pc_case = component.id
OR computer_system.ram = component.id
GROUP BY computer_system.name) AS SYSTEM
ON SYSTEM.name = computer_system.name;