Typical disclaimer: I *really* need to stress that everything below is based on my own research and findings - the information below does not indicate official documentation or support, however some of this may be accurate.
John
_____________________________
Background
When building SQL queries for K1000 reports, smart labels and ticket rules, it frequently becomes necessary to use JOIN statements so that data can be pulled from multiple tables. In turn, writing JOIN statements requires a knowledge of table relationships - specifically, which columns in two tables contain data common to both tables. At one point (several versions ago), a detailed schema diagram for the K1000 database was painstakingly developed, however (as might be inferred) it was not an automated process and required a large amount of manual tweaks to accurately depict all of the table and column relationships. As the database has expanded significantly since then, it has become infeasible to maintain this diagram, and most knowledge of table relationships is either gained by referencing wizard-generated queries or pure experience. While those approaches work, when you consider all of the K1000 users (customers and internal), the time and effort spent by all to determine these relationships is considerable.
I had been contemplating the situation for a some time now, and while researching in the database for a relationship to a column (inaccessible using the wizards), I discovered a list of all of the columns used in the K1000 database: INFORMATION_SCHEMA.COLUMNS. This table inspired me to write the following queries, which will hopefully save others some time in finessing these relationships from the ORG1 database.
Construction
Using the INFORMATION_SCHEMA.COLUMNS table, I developed the following queries to help illustrate the table relationships - specifically primary keys (for example, MACHINE.ID) and foreign keys (columns that typically contain the same data as a primary key column in another table - for example, HD_TICKET.MACHINE_ID or MACHINE_SOFTWARE_JT.MACHINE_ID). While these are not represented graphically, hopefully they can still be good starting points and save some people some time.
I worked under the (potentially faulty) premise that primary keys would typically be named TABLE.ID and foreign key columns would typically be named FOREIGN_TABLE.TABLE_ID. This approach doesn't work in every situation, but it does get most.
For example, there are situations where a foreign key isn't listed due to (apparently) non-standard naming and tagging conventions - one example being MACHINE_NIC.ID, which should probably be named MACHINE_NIC.MACHINE_ID and set as the primary key for the MACHINE_NIC table.
Building a filtered query is also not fully automatic, as some knowledge of table relationships is required to get the most out of it (for example, HD_TICKET.OWNER_ID and HD_TICKET.SUBMITTER_ID relate to USER.ID), so this should also be kept in mind when constructing filters.
SQL Queries
The following queries can be setup as SQL Reports on the K1000. When run, you should get output similar to the attached files.
1) K1000 Database Keys Relationship Matrix Query
Lists Primary and Foreign Keys for all K1000 Tables in the ORG1 Database.
SELECT
C.TABLE_NAME,
(
CASE
WHEN
PK.PRIMARY_KEYS
IS
NULL
THEN
'N/A'
ELSE
GROUP_CONCAT(
DISTINCT
PK.PRIMARY_KEYS
ORDER
BY
1 SEPARATOR
'\n'
)
END
)
AS
PRIMARY_KEYS,
(
CASE
WHEN
FK.FOREIGN_KEYS
IS
NULL
THEN
'N/A'
ELSE
GROUP_CONCAT(
DISTINCT
FK.FOREIGN_KEYS
ORDER
BY
1 SEPARATOR
'\n'
)
END
)
AS
FOREIGN_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
LEFT
JOIN
(
SELECT
C.TABLE_NAME,
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME)
AS
PRIMARY_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
WHERE
TABLE_SCHEMA =
'ORG1'
AND
COLUMN_KEY =
'pri'
)
PK
ON
PK.TABLE_NAME = C.TABLE_NAME
LEFT
JOIN
(
SELECT
C.TABLE_NAME,
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME)
AS
FOREIGN_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
WHERE
TABLE_SCHEMA =
'ORG1'
AND
COLUMN_KEY !=
'pri'
AND
COLUMN_NAME rlike
'_ID'
)
FK
ON
FK.TABLE_NAME = C.TABLE_NAME
WHERE
TABLE_SCHEMA =
'ORG1'
GROUP
BY
C.TABLE_NAME
ORDER
BY
C.TABLE_NAME, PK.PRIMARY_KEYS, FK.FOREIGN_KEYS
As an unfiltered query it’s a bit to digest, but if a filter is added near the end of each subquery it’s potentially very useful. As an example, I’ve filtered on MACHINE.ID (primary key) and MACHINE_ID (foreign keys) in the next report.
AND
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME) rlike
'machine.id|machine_id'
Lists Primary and Foreign Keys for all K1000 Tables in the ORG1 Database that contain either MACHINE.ID or MACHINE_ID to reflect Table relationships to MACHINE.ID.
To customize, filter in each subquery (AND CONCAT... statements) by changing "machine.id" and "machine_id" as desired.
SELECT
C.TABLE_NAME,
(
CASE
WHEN
PK.PRIMARY_KEYS
IS
NULL
THEN
'N/A'
ELSE
GROUP_CONCAT(
DISTINCT
PK.PRIMARY_KEYS
ORDER
BY
1 SEPARATOR
'\n'
)
END
)
AS
PRIMARY_KEYS,
(
CASE
WHEN
FK.FOREIGN_KEYS
IS
NULL
THEN
'N/A'
ELSE
GROUP_CONCAT(
DISTINCT
FK.FOREIGN_KEYS
ORDER
BY
1 SEPARATOR
'\n'
)
END
)
AS
FOREIGN_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
LEFT
JOIN
(
SELECT
C.TABLE_NAME,
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME)
AS
PRIMARY_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
WHERE
TABLE_SCHEMA =
'ORG1'
AND
COLUMN_KEY =
'pri'
AND
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME) rlike
'machine.id|machine_id'
)
PK
ON
PK.TABLE_NAME = C.TABLE_NAME
LEFT
JOIN
(
SELECT
C.TABLE_NAME,
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME)
AS
FOREIGN_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
WHERE
TABLE_SCHEMA =
'ORG1'
AND
COLUMN_KEY !=
'pri'
AND
COLUMN_NAME rlike
'_ID'
AND
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME) rlike
'machine.id|machine_id'
)
FK
ON
FK.TABLE_NAME = C.TABLE_NAME
WHERE
TABLE_SCHEMA =
'ORG1'
AND
(PK.PRIMARY_KEYS
AND
FK.FOREIGN_KEYS)
IS
NOT
NULL
GROUP
BY
C.TABLE_NAME
ORDER
BY
C.TABLE_NAME, PK.PRIMARY_KEYS, FK.FOREIGN_KEYS
3) K1000 Database Keys Relationship Matrix (Filtered on MACHINE.ID & MACHINE_ID, All Tables)
Lists Primary and Foreign Keys for all K1000 Tables in the ORG1 Database that contain either MACHINE.ID or MACHINE_ID to reflect Table relationships to MACHINE.ID. Lists all tables in the ORG1 Database, which can be useful if a full database view is desired.
To customize, filter in each subquery (AND CONCAT... statements) by changing "machine.id" and "machine_id" as desired.
SELECT
C.TABLE_NAME,
(
CASE
WHEN
PK.PRIMARY_KEYS
IS
NULL
THEN
'N/A'
ELSE
GROUP_CONCAT(
DISTINCT
PK.PRIMARY_KEYS
ORDER
BY
1 SEPARATOR
'\n'
)
END
)
AS
PRIMARY_KEYS,
(
CASE
WHEN
FK.FOREIGN_KEYS
IS
NULL
THEN
'N/A'
ELSE
GROUP_CONCAT(
DISTINCT
FK.FOREIGN_KEYS
ORDER
BY
1 SEPARATOR
'\n'
)
END
)
AS
FOREIGN_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
LEFT
JOIN
(
SELECT
C.TABLE_NAME,
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME)
AS
PRIMARY_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
WHERE
TABLE_SCHEMA =
'ORG1'
AND
COLUMN_KEY =
'pri'
AND
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME) rlike
'machine.id|machine_id'
)
PK
ON
PK.TABLE_NAME = C.TABLE_NAME
LEFT
JOIN
(
SELECT
C.TABLE_NAME,
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME)
AS
FOREIGN_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
WHERE
TABLE_SCHEMA =
'ORG1'
AND
COLUMN_KEY !=
'pri'
AND
COLUMN_NAME rlike
'_ID'
AND
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME) rlike
'machine.id|machine_id'
)
FK
ON
FK.TABLE_NAME = C.TABLE_NAME
WHERE
TABLE_SCHEMA =
'ORG1'
GROUP
BY
C.TABLE_NAME
ORDER
BY
C.TABLE_NAME, PK.PRIMARY_KEYS, FK.FOREIGN_KEYS
Baseline Queries
For anyone that might be interested in how I developed the above queries (or for those who just want a list of primary keys or foreign keys for each table), here are the baseline queries I started with. To combine these, I used the INFORMATION_SCHEMA.COLUMNS table in the main query and joined these focused queries using the TABLE_NAME column for the JOIN criteria.
PK) Baseline Primary Key Query
Lists Primary Keys for all K1000 Tables in the ORG1 Database.
SELECT
C.TABLE_NAME,
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME)
AS
PRIMARY_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
WHERE
TABLE_SCHEMA =
'ORG1'
AND
COLUMN_KEY =
'pri'
Lists Primary Keys for all K1000 Tables in the ORG1 Database that contain either MACHINE.ID or MACHINE_ID.
SELECT
C.TABLE_NAME,
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME)
AS
PRIMARY_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
WHERE
TABLE_SCHEMA =
'ORG1'
AND
COLUMN_KEY =
'pri'
AND
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME) rlike
'machine.id|machine_id'
Lists Foreign Keys for all K1000 Tables in the ORG1 Database.
SELECT
C.TABLE_NAME,
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME)
AS
FOREIGN_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
WHERE
TABLE_SCHEMA =
'ORG1'
AND
COLUMN_KEY !=
'pri'
AND
COLUMN_NAME rlike
'_ID'
Lists Foreign Keys for all K1000 Tables in the ORG1 Database that contain either MACHINE.ID or MACHINE_ID.
SELECT
C.TABLE_NAME,
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME)
AS
FOREIGN_KEYS
FROM
INFORMATION_SCHEMA.COLUMNS C
WHERE
TABLE_SCHEMA =
'ORG1'
AND
COLUMN_KEY !=
'pri'
AND
COLUMN_NAME rlike
'_ID'
AND
CONCAT(C.TABLE_NAME,
'.'
, C.COLUMN_NAME) rlike
'machine.id|machine_id'
John - jverbosk 9 years ago