Check and get Parent Tables - Firebird - sql

Anyone can help me a bit?
I use this Query to get all the constraint names, foreign tables, fields with their contraint tables and fields...
select distinct Con.rdb$constraint_name,
Rel.rdb$relation_name TableName ,Rel.rdb$field_name FieldName,
FCon.rdb$Relation_Name ForeignTableName, FIseg.rdb$Field_Name ForeignFieldName
from rdb$relation_fields Rel
inner join rdb$relation_constraints Con on (Con.rdb$relation_name = Rel.rdb$relation_name and Con.rdb$constraint_type like 'FOREIGN%')
inner join rdb$indices IDX on (IDX.rdb$index_name = Con.rdb$index_name)
inner join rdb$index_segments ISeg on (ISeg.rdb$index_name = Idx.rdb$index_name and ISeg.rdb$Field_Name = Rel.rdb$field_name)
inner join rdb$Relation_Constraints FCon on (FCon.rdb$index_name = Idx.rdb$Foreign_Key)
inner join rdb$index_segments FIseg on (FISeg.rdb$index_name = Idx.rdb$Foreign_key and FISeg.rdb$Field_Position = ISeg.rdb$Field_Position)
where Rel.rdb$relation_name not like 'RDB$%' and
FCon.rdb$Relation_Name <> Rel.rdb$relation_name and
FCon.rdb$Relation_Name = :TABLENAME
I would like to check whether the specified table (:TABLENAME) has a parent table or not and I need it's name, foreignfieldname etc like in my first query.
For example:
Applications -> Licenses -> Licenseinfos -> ''
-> Registrations -> ''
If I add Licenseinfos I would like to get "Licenses"
If I add Registrations I would like to get "Licenses"
Thanks for the answers! I use firebird 2.5

The simplest way to do this is using the following query. It identifies both sides of the constraint using the table RDB$REF_CONSTRAINTS, this doesn't need to do anything with the RDB$INDICES table (which would be a complication if you had keys with multiple columns):
select
PK.RDB$RELATION_NAME as PKTABLE_NAME
,FK.RDB$RELATION_NAME as FKTABLE_NAME
from RDB$RELATION_CONSTRAINTS FK
inner join RDB$REF_CONSTRAINTS RC on FK.RDB$CONSTRAINT_NAME = RC.RDB$CONSTRAINT_NAME
inner join RDB$RELATION_CONSTRAINTS PK on PK.RDB$CONSTRAINT_NAME = RC.RDB$CONST_NAME_UQ
where FK.RDB$RELATION_NAME = :TABLENAME

Related

Join two sys.columns tables from different servers

I have two similar dbs on two different servers. I'm trying to join their sys.columns tables, but I don't understand why this wouldn't work
SELECT *
FROM server1.db1.sys.columns t
INNER JOIN server2.db2.sys.columns s
ON OBJECT_NAME(t.[object_id]) = OBJECT_NAME(s.[object_id])
AND t.name = s.name
WHERE OBJECT_NAME(t.[object_id]) = 'commonTable'
commonTable exists in both dbs. Above query returns empty set
I join on OBJECT_NAME because their object_ids obviously different, since they are located on different dbs, so I join on their names in that way
OBJECT_NAME() works locally so it is never going to return the name of some object id in another database. Join to the foreign sys.columns and sys.objects view.
SELECT *
FROM server1.db1.sys.columns AS loc_c
INNER JOIN server2.db2.sys.columns AS rem_c
ON loc_c.name = rem_c.name
INNER JOIN server1.db1.sys.tables AS loc_t
ON loc_t.[object_id] = loc_c.[object_id]
INNER JOIN server2.db2.sys.tables AS rem_t
ON loc_t.name = rem_t.name
AND rem_t.[object_id] = rem_c.[object_id]
WHERE loc_t.name = N'commonTable';
You may want to add local and remote joins to sys.schemas, too, since dbo.foo and otherschema.foo will both match.
You may also consider synonyms and/or views to reduce complexity, if you are doing this a lot.
CREATE VIEW dbo.server1cols
AS
SELECT [table] = loc_t.name, [column] = loc_c.name
FROM server1.db1.sys.columns AS loc_c
INNER JOIN server1.db1.sys.tables AS loc_t
ON loc_t.[object_id] = loc_c.[object_id];
GO
CREATE VIEW dbo.server2cols
AS
SELECT [table] = rem_t.name, [column] = rem_c.name
FROM server2.db2.sys.columns AS rem_c
INNER JOIN server2.db2.sys.tables AS rem_t
ON rem_t.[object_id] = rem_c.[object_id];
GO
CREATE VIEW dbo.MatchDB1DB2Cols
AS
SELECT s1.[table],
db1column = s1.[column],
db2column = s2.[column]
FROM dbo.server1cols AS s1
INNER JOIN dbo.server2cols AS s2
ON s1.[table] = s2.[table]
AND s1.[column] = s2.[column];
GO
Now your query is simply:
SELECT [table], db1column, db2column
FROM dbo.MatchDB1DB2Cols
WHERE [table] = N'commonTable';
You may also want to consider a full outer join somewhere so you can also note differences between the tables.

Two SQL Queries With Some Overlapping Data

I'm trying to combine two different SQL queries into one table. I've tried various joins and Union but it either duplicates rows or doesn't show all of them.
The first query is
Select
HW.DisplayName,
HW.LocationDetails_0B39A057_2BE8_11B2_BBE2_1E03564AA5CA,
HW.Notes_5CFC0E2A_AB82_5830_D4BB_0596CBED1984
FROM MT_Cireson$AssetManagement$HardwareAsset HW
where HardwareAssetStatus_3019ADDF_4F3D_2C55_2024_72C22E11F4CF = '866879DF-8FB6-E521-F0E3-FEF86EE1BC92'
This gives all of my hardware assets that have the status I'm looking for.
The second query is:
SELECT
hw.DisplayName,
HW.LocationDetails_0B39A057_2BE8_11B2_BBE2_1E03564AA5CA,
HW.Notes_5CFC0E2A_AB82_5830_D4BB_0596CBED1984,
UB.UPN_7641DFF7_7A20_DC04_FC1C_B6FA8715DA02
FROM MT_Cireson$AssetManagement$HardwareAsset HW
inner join Relationship Rel on HW.BaseManagedEntityId = Rel.SourceEntityId
inner join RelationshipType RT on RT.RelationshipTypeId = Rel.RelationshipTypeId
inner join MT_Microsoft$AD$UserBase UB on UB.BaseManagedEntityId = Rel.TargetEntityId
where RT.RelationshipTypeName = 'Cireson.AssetManagement.HardwareAssetHasPrimaryUser'
and HardwareAssetStatus_3019ADDF_4F3D_2C55_2024_72C22E11F4CF = '866879DF-8FB6-E521-F0E3-FEF86EE1BC92'
This gives all of the hardware assets I'm looking for that have a primary user configured, but doesn't give the assets without a primary user. I'm not sure how to either A: combine the results just putting in NULL as a primary user for records that don't have one, or B: actually query all the assets at one time and include the primary user column.
I didn't write the second query and I'm not sure exactly how it works. I've tried doing union between the queries but that duplicates the rows because the first query already contains all the elements in the second.
Edit: The PrimaryUser comes from the MT_Microsoft$AD$UserBase table. I've tried adding another column to the first and just setting it as null like:
null as primaryUser,
How about a LEFT JOIN to include all records from HW that are not in UB:
SELECT
hw.DisplayName,
HW.LocationDetails_0B39A057_2BE8_11B2_BBE2_1E03564AA5CA,
HW.Notes_5CFC0E2A_AB82_5830_D4BB_0596CBED1984,
UB.UPN_7641DFF7_7A20_DC04_FC1C_B6FA8715DA02
FROM
MT_Cireson$AssetManagement$HardwareAsset HW
INNER JOIN
Relationship Rel
ON
HW.BaseManagedEntityId = Rel.SourceEntityId
INNER JOIN
RelationshipType RT
ON
RT.RelationshipTypeId = Rel.RelationshipTypeId
LEFT JOIN
MT_Microsoft$AD$UserBase UB
ON
UB.BaseManagedEntityId = Rel.TargetEntityId
WHERE
RT.RelationshipTypeName = 'Cireson.AssetManagement.HardwareAssetHasPrimaryUser'
AND HardwareAssetStatus_3019ADDF_4F3D_2C55_2024_72C22E11F4CF = '866879DF-8FB6-E521-F0E3-FEF86EE1BC92'
UPDATE:
If the null primary users is what you want, I would recraft the query like:
SELECT
hw.DisplayName,
HW.LocationDetails_0B39A057_2BE8_11B2_BBE2_1E03564AA5CA,
HW.Notes_5CFC0E2A_AB82_5830_D4BB_0596CBED1984,
UB.UPN_7641DFF7_7A20_DC04_FC1C_B6FA8715DA02
FROM
MT_Cireson$AssetManagement$HardwareAsset HW
LEFT JOIN
MT_Microsoft$AD$UserBase UB
ON
HW.BaseManagedEntityId = UB.SourceEntityId
INNER JOIN
RelationshipType RT
ON
RT.RelationshipTypeId = Rel.RelationshipTypeId
INNER JOIN
Relationship Rel
ON
UB.BaseManagedEntityId = Rel.TargetEntityId
WHERE
RT.RelationshipTypeName = 'Cireson.AssetManagement.HardwareAssetHasPrimaryUser'
AND HardwareAssetStatus_3019ADDF_4F3D_2C55_2024_72C22E11F4CF = '866879DF-8FB6-E521-F0E3-FEF86EE1BC92'
I LEFT JOIN'ed HW and UB tables.
As I said earlier, you'll have to tweak the joins. I would try a LEFT JOIN on all tables.
you have an extra column in your 1st query that's why you have duplicates in your union. I would suggest using CTE, there might be other better efficient solutions out there.
;WITH query1
AS
(
SELECT col1, col2
FROM table
),
query2 AS
(
SELECT col1, col2
FROM table
)
SELECT *
FROM cteTable1
UNION ALL
SELECT *
FROM cteTable2
WHERE NOT EXISTS(SELECT * FROM cteTable1 WHERE cteTable1.col1 = cteTable2.col2)

Deadlock - Lock of a column whereas there is no data

I have a deadlock when I execute this stored procedure :
-- Delete transactions
delete from ADVICESEQUENCETRANSACTION
where ADVICESEQUENCETRANSACTION.id in (
select TR.id from ADVICESEQUENCETRANSACTION TR
inner join ACCOUNTDESCRIPTIONITEM IT on TR.ACCOUNTDESCRIPTIONITEMID = IT.id
inner join ACCOUNTDESCRIPTION ACC on IT.ACCOUNTDESCRIPTIONID = ACC.id
inner join RECOMMENDATIONDESCRIPTION RD on ACC.RECOMMENDATIONDESCRIPTIONID = RD.id
inner join RECOMMENDATION REC on REC.id = RD.RECOMMENDATIONID
inner join ADVICESEQUENCE ADV on ADV.id = REC.ADVICESEQUENCEID
where adv.Id = #AdviceSequenceId AND (#RecommendationState is NULL OR #RecommendationState=REC.[State])
);
Here is the schema of the table :
Here is the deadlock graph :
you can see the detail of the deadlock graph here
So, when I retrieve the associatedobjid of the ressource node, I identify that it's the primary key and an index of the table AdviceSequenceTransaction :
SELECT OBJECT_SCHEMA_NAME([object_id]), * ,
OBJECT_NAME([object_id])
FROM sys.partitions
WHERE partition_id = 72057595553120256 OR partition_id = 72057595553316864;
SELECT name FROM sys.indexes WHERE object_id = 31339176 and (index_id = 1 or index_id = 4)
PK_AdviceSequenceTransaction
IX_ADVICESEQUENCEID_ADVICE
As there is a relation on the table AdviceSequenceTransaction on the key ParentTransactionId and the key Primary key, I have created an index on the column ParentTransactionId.
And I have no more Deadlock. But the problem is I don't know exactly why there is no more deadlock :-/
Moreover, on the set of data to test it, there is no data in ParentTransactionId. All are NULL.
So, Even is there no data (null) in the ParentTransactionId, is there an access to the Primary key by SQL Server ???
An other thing is that I want to remove a join in the delete statement :
delete from ADVICESEQUENCETRANSACTION
where ADVICESEQUENCETRANSACTION.id in (
select TR.id from ADVICESEQUENCETRANSACTION TR
inner join ACCOUNTDESCRIPTIONITEM IT on TR.ACCOUNTDESCRIPTIONITEMID = IT.id
inner join ACCOUNTDESCRIPTION ACC on IT.ACCOUNTDESCRIPTIONID = ACC.id
inner join RECOMMENDATIONDESCRIPTION RD on ACC.RECOMMENDATIONDESCRIPTIONID = RD.id
inner join RECOMMENDATION REC on REC.id = RD.RECOMMENDATIONID
inner join ADVICESEQUENCE ADV on ADV.id = REC.ADVICESEQUENCEID
where adv.Id = #AdviceSequenceId AND (#RecommendationState is NULL OR #RecommendationState=REC.[State])
);
into :
delete from ADVICESEQUENCETRANSACTION
where ADVICESEQUENCETRANSACTION.id in (
select TR.id from ADVICESEQUENCETRANSACTION TR
inner join ACCOUNTDESCRIPTIONITEM IT on TR.ACCOUNTDESCRIPTIONITEMID = IT.id
inner join ACCOUNTDESCRIPTION ACC on IT.ACCOUNTDESCRIPTIONID = ACC.id
inner join RECOMMENDATIONDESCRIPTION RD on ACC.RECOMMENDATIONDESCRIPTIONID = RD.id
inner join RECOMMENDATION REC on REC.id = RD.RECOMMENDATIONID
where TR.AdviceSequenceId = #AdviceSequenceId AND (#RecommendationState is NULL OR #RecommendationState=REC.[State])
);
I removed the last join. But if I do this, I have again the deadlock ! And here again, I don't know why...
Thank you for your enlightment :)
Using a complex, compound join in your WHERE clause can often cause problems. SQL Server processes the clauses using the following Logical Processing Order (view here):
FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP
Using views, or derived tables or views greatly reduces the number of iterative (TABLE) scans required to obtain the desired result because the emphasis in your query is better aligned to use the logical execution order. The FROM clause for each of the derived tables (or views) is executed first, limiting the result set passed to the ON cause, then the JOIN clause and so on, because you're passing your parameters to an "inside FROM" rather than the "outermost WHERE".
So your code could look something like this:
delete from (SELECT ADVICESEQUENCETRANSACTION
FROM (SELECT tr.id
FROM ADVICESEQUENCETRANSACTION WITH NOLOCK
WHERE AdviceSequenceId = #AdviceSequenceId
)TR
INNER JOIN (SELECT [NEXT COLUMN]
FROM [NEXT TABLE] WITH NOLOCK
WHERE COLUMN = [PARAM]
)B
ON TR.COL = B.COL
)ALIAS
WHERE [COLUMN] = COL.PARAM
);
and so on...
(I know the code isn't cut and paste usable, but it should serve to convey the general idea)
In this way, you're passing your parameters to "inner queries" first, pre-loading your limited result set (particularly if you should use views), and then working outward. Using Locking Hints everywhere appropriate will also help to prevent some of the problems you might otherwise encounter. This technique can also help make the execution plan more effective in helping you diagnose where your blocks are coming from, should you still have any.
one approach is to use WITH (nolock) on table ADVICESEQUENCETRANSACTION TR if you dont mind dirty reads.

Join different tables depending on field values

I have the following sql view result which contains the audited changes for every field for every table in the system:
In this case the above image tell us that both read and write permissions were revoked for the user Lucas for the subscription called MySubscription.
I need to display that info in a grid however that is not what I want to display, I mean, I donĀ“t want to display IDs. I need to display "Read" instead of 50, "Write" instead of 51, "Lucas" instead of 1 and "MySubscription" instead of 6.
To do that I would like to improve the sql view to get the values instead of their IDs as I mention above. The result that I am looking for is this one:
The database contains the tables Subscriptions, ProductPermissions and TenantUsers to get the needed info using joins.
Could you please give me some clues about how could I achieve what I need? Thank you.
You could do this with a series of LEFT JOINs, some casting might be required to get the datatype of the joining column the same as NewValue (I've assumed a column called Name in all your joining tables, this may need changing):
SELECT a.AuditLogId,
a.Operation,
a.[Table],
a.RowId,
a.Name,
[OldValue] = COALESCE(s_Old.Name, pp_old.Name, t_Old.Name),
[NewValue] = COALESCE(s_New.Name, pp_New.Name, t_New.Name)
FROM AuditLog a
LEFT JOIN Subscriptions s_Old
ON a.OldValue = CAST(s_Old.SubscriptionID AS VARCHAR)
AND a.Name = 'SubscriptionID'
LEFT JOIN ProductPermissions pp_Old
ON a.OldValue = CAST(p_Old.ProductPermissionID AS VARCHAR)
AND a.Name = 'ProductPermissionId'
LEFT JOIN TenantUsers t_Old
ON a.OldValue = CAST(t_Old.TenantUserId AS VARCHAR)
AND a.Name = 'TenantUsers'
LEFT JOIN Subscriptions s_New
ON a.NewValue = CAST(s_New.SubscriptionID AS VARCHAR)
AND a.Name = 'SubscriptionID'
LEFT JOIN ProductPermissions pp_New
ON a.NewValue = CAST(p_New.ProductPermissionID AS VARCHAR)
AND a.Name = 'ProductPermissionId'
LEFT JOIN TenantUsers t_New
ON a.NewValue = CAST(t_New.TenantUserId AS VARCHAR)
AND a.Name = 'TenantUsers'
If required you could then PIVOT this into one row per transaction:
SELECT a.AuditLogId,
a.Operation,
a.[Table],
a.RowId,
[OldSubscriptionValue] = MAX(s_old.Name),
[OldProductPermissionValue] = MAX(pp_old.Name),
[OldTennantUserValue] = MAX(t_old.Name),
[NewSubscriptionValue] = MAX(s_New.Name),
[NewProductPermissionValue] = MAX(pp_New.Name),
[NewTennantUserValue] = MAX(t_New.Name)
FROM AuditLog a
LEFT JOIN Subscriptions s_Old
ON a.OldValue = CAST(s_Old.SubscriptionID AS VARCHAR)
AND a.Name = 'SubscriptionID'
LEFT JOIN ProductPermissions pp_Old
ON a.OldValue = CAST(p_Old.ProductPermissionID AS VARCHAR)
AND a.Name = 'ProductPermissionId'
LEFT JOIN TenantUsers t_Old
ON a.OldValue = CAST(t_Old.TenantUserId AS VARCHAR)
AND a.Name = 'TenantUsers'
LEFT JOIN Subscriptions s_New
ON a.NewValue = CAST(s_New.SubscriptionID AS VARCHAR)
AND a.Name = 'SubscriptionID'
LEFT JOIN ProductPermissions pp_New
ON a.NewValue = CAST(p_New.ProductPermissionID AS VARCHAR)
AND a.Name = 'ProductPermissionId'
LEFT JOIN TenantUsers t_New
ON a.NewValue = CAST(t_New.TenantUserId AS VARCHAR)
AND a.Name = 'TenantUsers'
GROUP BY a.AuditLogId, a.Operation, a.[Table], a.RowId;
It is a pretty dirty solution, I would be inclined to store this data in the format you want to select it in i.e. instead of 50/51, store read/write directly in the NewValue column.
Alternatively, if you did want the second format with one row per transaction then I'd be inclined to store it in this way. It could be worth reading about the Entity-Attribute-Value Antipattern.
You could PIVOT the data and LEFT JOIN to the lookup tables. Then UNPIVOT if necessary.
If you simply need to display words instead of numbers, create a lookup table that maps between the number and the word you want to display:
create table NewValueText (
NewValue integer,
Description char(20)
);
insert into NewValueText values
(50, 'Read'),
(51, 'Write'); --etc.
--MyTable is your table that contains the NewValue column.
select Description from MyTable
inner join NewValueText on MyTable.NewValue = NewValueText.NewValue;
You could try joining in your tables where your values are specific. You'll need to create a look-up table for other values other than Really it's a dirty solution because it means hard coding your NewValue fields into the SQL:
...
FROM
audit LEFT JOIN
mysubscription ON audit.rowid = mysubscription.tenantuserid
AND audit.newvalue = 1
LEFT JOIN
lookup_list ON audit.rowid = lookup_list.lookup_id
AND audit.newvalue <> 1
...
That may work for you.

Postgresql Query with joins

I have 6 tables that I'm trying to connect together in 1 query; however, I can't quite figure out how to write the query.
Here are my tables in bold and the columns that they contain.
pg
pgid
ipg
ipgid
roid
pgid
ug
ugid
iug
iuid
roid
ugid
ro
roid
inid
in
inid
My goal is to output the inid field given certain criteria from the pg and ug tables. I want to find all the inid entries where pg.pgid=1 and ug.ugid=2. This involves a a lot of joins and I think some select in statments but I'm not familiar enough with how to piece that all together.
Here is what I have so far...
SELECT inid
FROM in
INNER JOIN ro ON in.inid = ro.inid
INNER JOIN iug ON ro.roid = iug.roid
INNER JOIN ug ON iug.ugid = ug.ugid
INNER JOIN ipg ON ro.roid = ipg.roid
INNER JOIN pg ON ipg.pgid = pg.pgid
WHERE pg.pgid = 1
AND ug.ugid = 2
You cannot name your table in, that is a reserved word in every SQL standard and in PostgreSQL. You will get a syntax error if you try. You can still force the system to accept it if you double quote the name CREATE TABLE "in" ... but then you have to remember to double-quote it every time you use it and you will get confusing error messages. In short: don't do that.
I renamed the table tbl_in and gave it an alias in the query.
Also, inid is ambiguous as SELECT item. Either use USING as join condition (then only one column inid is in the result set) or table-qualify the column name.
Otherwise the query looks fine:
SELECT i.inid -- ambiguous without table-qualification
FROM tbl_in i -- renamed table
JOIN ro ON i.inid = ro.inid
JOIN iug ON ro.roid = iug.roid
JOIN ug ON iug.ugid = ug.ugid
JOIN ipg ON ro.roid = ipg.roid
JOIN pg ON ipg.pgid = pg.pgid
WHERE pg.pgid = 1
AND ug.ugid = 2;
If you have foreign key constraints between pg and ipg and between ug and iug, then you can simplify:
SELECT i.inid -- ambiguous without table-qualification
FROM tbl_in i -- renamed table
JOIN ro ON i.inid = ro.inid
JOIN iug ON ro.roid = iug.roid
JOIN ipg ON ro.roid = ipg.roid
WHERE ipg.pgid = 1
AND iug.ugid = 2;
... because once found in iug / ipg the values are guaranteed to be present in up / pg.