This post collects practical SQL queries for TIBCO Spotfire Server administrators to analyze library content, usage, and users directly from the repository and Action Log databases.
Environment and schema assumptions:
- Spotfire Server repository tables: LIB_ITEMS, LIB_ITEM_TYPES, USERS, GROUPS, GROUP_MEMBERS, LIB_ACCESS
- Optional Action Log database with ACTIONLOG table (enabled via Spotfire Server configuration)
- Standard columns like CONTENT_SIZE, ACCESSED, LAST_LOGIN available in your version
- SQL Server syntax used (adjust functions for Oracle or PostgreSQL)
Always test queries in non-production environment first and adapt to your exact schema and Spotfire version.
1. Library Item Size (Largest Items)
This query lists all library items with their size in MB, sorted by size so you can quickly identify the largest analyses, SBDF files, and other objects.
SELECT
li.ITEM_ID,
li.TITLE,
lit.DISPLAY_NAME AS ITEM_TYPE,
li.CONTENT_SIZE / (1024*1024.0) AS SIZE_MB,
u1.DISPLAY_NAME AS CREATED_BY,
li.CREATED,
u2.DISPLAY_NAME AS MODIFIED_BY,
li.MODIFIED,
li.ACCESSED,
parent.TITLE AS PARENT_FOLDER
FROM LIB_ITEMS li
JOIN LIB_ITEM_TYPES lit
ON li.ITEM_TYPE = lit.TYPE_ID
LEFT JOIN LIB_ITEMS parent
ON li.PARENT_ID = parent.ITEM_ID
LEFT JOIN USERS u1
ON li.CREATED_BY = u1.USER_ID
LEFT JOIN USERS u2
ON li.MODIFIED_BY = u2.USER_ID
ORDER BY li.CONTENT_SIZE DESC;
Use this to drive cleanup of large, rarely used library items such as old analyses and obsolete SBDF files.
2. Library / Folder Size
This query aggregates CONTENT_SIZE at the folder level, giving you a view of which folders consume the most space.
SELECT
li.PARENT_ID AS FOLDER_ITEM_ID,
parent.TITLE AS FOLDER_TITLE,
SUM(li.CONTENT_SIZE) / (1024*1024.0) AS FOLDER_SIZE_MB,
parent.CREATED,
parent.MODIFIED,
parent.ACCESSED
FROM LIB_ITEMS li
JOIN LIB_ITEMS parent
ON li.PARENT_ID = parent.ITEM_ID
GROUP BY
li.PARENT_ID,
parent.TITLE,
parent.CREATED,
parent.MODIFIED,
parent.ACCESSED
ORDER BY FOLDER_SIZE_MB DESC;
Run this periodically to identify candidate folders for archive or refactoring.
3. Report Utilization (Recently Accessed Reports)
The next query lists analyses (DXP files) that have been accessed in the last 30 days, using the ACCESSED timestamp in LIB_ITEMS.
SELECT
li.ITEM_ID,
li.TITLE,
li.ACCESSED
FROM LIB_ITEMS li
JOIN LIB_ITEM_TYPES lit
ON li.ITEM_TYPE = lit.TYPE_ID
WHERE lit.FILE_SUFFIX = 'dxp'
AND li.ACCESSED >= DATEADD(DAY, -30, GETUTCDATE())
ORDER BY li.ACCESSED DESC;
You can adjust the DATEADD window to whatever "recently used" means in your environment.
4. Idle Reports (Candidates for Archive)
To find analyses that have not been accessed in the last 90 days (or at all), use this query.
SELECT
li.ITEM_ID,
li.TITLE,
li.ACCESSED,
li.MODIFIED
FROM LIB_ITEMS li
JOIN LIB_ITEM_TYPES lit
ON li.ITEM_TYPE = lit.TYPE_ID
WHERE lit.FILE_SUFFIX = 'dxp'
AND (
li.ACCESSED IS NULL
OR li.ACCESSED < DATEADD(DAY, -90, GETUTCDATE())
)
ORDER BY li.ACCESSED,
li.MODIFIED;
This is a good starting point for a "report retirement" process in collaboration with business owners.
5. Groups and Their Members
The next query gives a simple view of which users belong to which Spotfire groups, based on the GROUPS, GROUP_MEMBERS, and USERS tables.
SELECT
g.GROUP_NAME,
u.USER_NAME,
u.DISPLAY_NAME
FROM GROUPS g
JOIN GROUP_MEMBERS gm
ON g.GROUP_ID = gm.GROUP_ID
JOIN USERS u
ON gm.USER_ID = u.USER_ID
ORDER BY
g.GROUP_NAME,
u.USER_NAME;
Use this output to audit access models and compare against your identity provider structure.
6. Access Groups Attached to Folders and Items
To see which groups have permissions on which folders and items, query LIB_ACCESS and join to LIB_ITEMS and GROUPS.
SELECT
parent.TITLE AS FOLDER_TITLE,
li.TITLE AS ITEM_TITLE,
g.GROUP_NAME,
a.PERMISSION AS PERMISSION_MASK
FROM LIB_ACCESS a
JOIN LIB_ITEMS li
ON a.ITEM_ID = li.ITEM_ID
LEFT JOIN LIB_ITEMS parent
ON li.PARENT_ID = parent.ITEM_ID
JOIN GROUPS g
ON a.GROUP_ID = g.GROUP_ID
ORDER BY
parent.TITLE,
li.TITLE,
g.GROUP_NAME;
If your schema has a dedicated "folder" flag or type, add a filter to focus on folders only.
7. Usage Tracking with Action Logs
Once the Action Log feature is enabled, the ACTIONLOG table becomes your main source for high-fidelity usage tracking.
Daily usage summary by category and action:
SELECT
CAST(LOGGED_TIME AS date) AS LOG_DATE,
LOG_CATEGORY,
LOG_ACTION,
COUNT(*) AS EVENT_COUNT
FROM ACTIONLOG
WHERE LOG_CATEGORY IN ('auth', 'library')
GROUP BY
CAST(LOGGED_TIME AS date),
LOG_CATEGORY,
LOG_ACTION
ORDER BY
LOG_DATE,
LOG_CATEGORY,
LOG_ACTION;
This gives you a quick overview of logins, report opens, and other key activities across time.
8. Report Open Events (Action Log Detail)
To analyze which reports are opened most frequently, filter the ACTIONLOG entries on library-related actions.
SELECT
a.OBJECT_NAME AS REPORT_NAME,
COUNT(*) AS OPEN_COUNT
FROM ACTIONLOG a
WHERE a.LOG_CATEGORY = 'library'
AND a.LOG_ACTION = 'open'
GROUP BY
a.OBJECT_NAME
ORDER BY
OPEN_COUNT DESC;
Column names and LOG_ACTION values can differ by version, so check your own table definition first.
9. Unique Users Over a Period
The following query returns the number of distinct users who logged in during the last 90 days, based on Action Log authentication events.
SELECT
COUNT(DISTINCT USER_NAME) AS UNIQUE_USERS_90D
FROM ACTIONLOG
WHERE LOG_CATEGORY = 'auth'
AND LOG_ACTION = 'login'
AND LOGGED_TIME >= DATEADD(DAY, -90, GETUTCDATE());
You can change the 90-day window or group by date to build adoption dashboards in Spotfire itself.
10. Idle Users (No Recent Logins)
If your USERS table maintains a LAST_LOGIN column, you can identify enabled users who have not logged in recently.
SELECT
USER_NAME,
DISPLAY_NAME,
LAST_LOGIN
FROM USERS
WHERE ENABLED = 1
AND (
LAST_LOGIN IS NULL
OR LAST_LOGIN < DATEADD(DAY, -90, GETUTCDATE())
)
ORDER BY LAST_LOGIN;
Where LAST_LOGIN is not reliable, compute last login from the Action Log by taking the max LOGGED_TIME per user for auth/login events.
11. Duplicate Content Detection (Same Title in Different Folders)
Identify potential duplicate or redundant analyses across the library to help with content consolidation and cleanup.
SELECT
li.TITLE,
COUNT(*) AS COPIES,
STRING_AGG(parent.TITLE, ', ') AS FOLDER_LOCATIONS,
SUM(li.CONTENT_SIZE) / (1024*1024.0) AS TOTAL_SIZE_MB
FROM LIB_ITEMS li
LEFT JOIN LIB_ITEMS parent
ON li.PARENT_ID = parent.ITEM_ID
JOIN LIB_ITEM_TYPES lit
ON li.ITEM_TYPE = lit.TYPE_ID
WHERE lit.FILE_SUFFIX = 'dxp'
GROUP BY li.TITLE
HAVING COUNT(*) > 1
ORDER BY COPIES DESC;
Note: This example uses STRING_AGG for SQL Server (2017+). For Oracle, use LISTAGG instead.
12. Information Links and Data Source Dependencies
Track which analyses depend on specific data sources to understand the impact of data source changes.
SELECT
ds.TITLE AS DATA_SOURCE_NAME,
ds.CREATED,
ds.MODIFIED,
COUNT(DISTINCT li.ITEM_ID) AS REFERENCED_COUNT
FROM LIB_ITEMS ds
LEFT JOIN LIB_DATA ld
ON ds.ITEM_ID = ld.ITEM_ID
LEFT JOIN LIB_ITEMS li
ON li.CREATED_BY = ds.CREATED_BY
OR li.MODIFIED_BY = ds.MODIFIED_BY
WHERE ds.TITLE LIKE '%DataSource%'
OR ds.TITLE LIKE '%Connection%'
GROUP BY ds.ITEM_ID, ds.TITLE, ds.CREATED, ds.MODIFIED
ORDER BY REFERENCED_COUNT DESC;
This is useful for impact analysis when planning data source migrations or decommissioning. Modify the WHERE clause to match your data source naming conventions.
13. Storage Trend Analysis (Items By Creation Date)
Analyze library growth patterns to understand when content was created and identify opportunities for archival based on age.
SELECT
DATEPART(YEAR, li.CREATED) AS YEAR_CREATED,
DATEPART(MONTH, li.CREATED) AS MONTH_CREATED,
COUNT(*) AS ITEM_COUNT,
SUM(li.CONTENT_SIZE) / (1024*1024.0) AS TOTAL_SIZE_MB,
AVG(li.CONTENT_SIZE) / (1024.0) AS AVG_SIZE_KB
FROM LIB_ITEMS li
JOIN LIB_ITEM_TYPES lit
ON li.ITEM_TYPE = lit.TYPE_ID
WHERE lit.FILE_SUFFIX IN ('dxp', 'sbdf')
AND li.CREATED IS NOT NULL
GROUP BY
DATEPART(YEAR, li.CREATED),
DATEPART(MONTH, li.CREATED)
ORDER BY
YEAR_CREATED DESC,
MONTH_CREATED DESC;
This helps identify when your library grew most rapidly and supports governance planning.
Building an Admin Dashboard in Spotfire
These queries are ideal building blocks for a dedicated "Spotfire Admin" analysis. Typical approach:
- Create one or more information links (or data connections with custom SQL) using the queries above.
- Build pages for: library inventory, size & cleanup, report utilization, user & group audit, and usage/adoption stats.
- Run the queries read-only and schedule refreshes as needed so that admins always see current metadata without impacting production usage.
Conclusion
Regular monitoring of Spotfire library content, usage patterns, and user access is critical for maintaining a healthy and efficient analytics platform. The SQL queries provided in this post allow administrators to:
- Identify large, underutilized content candidates for retirement
- Track user adoption and engagement
- Audit permissions and group memberships
- Analyze data source and information link dependencies
- Build data-driven governance dashboards
- Detect duplicate or redundant content
For Spotfire Server administrators responsible for library and user management, these queries form the foundation of an effective metadata and governance toolkit.
Always remember to:
- Test in non-production environments first
- Adapt column names and logic to match your specific Spotfire version and database schema
- Use read-only accounts when running these queries directly against the repository
- Monitor query performance on large libraries and consider indexed views for frequently accessed queries
Happy querying!
Additional SQL Queries from TIBCO Resources
This section includes additional queries sourced from TIBCO Support and the Spotfire Community, featuring both SQL Server and Oracle syntax variations.
14. Largest Library Items (TIBCO Support)
Comprehensive query to identify the largest content items with details about creator, modification history, and parent folder location.
SELECT
li.CONTENT_SIZE/(1024*1024) AS [CONTENT_SIZE (MB)],
li.ITEM_ID,
li.TITLE,
li.DESCRIPTION,
lit.DISPLAY_NAME,
created.DISPLAY_NAME AS [CREATED BY],
li.CREATED AS [CREATED DATE],
modified.DISPLAY_NAME AS [MODIFIED BY],
li.MODIFIED AS [MODIFIED DATE],
li.ACCESSED AS [ACCESSED DATE],
li.PARENT_ID,
parent.TITLE AS [PARENT TITLE]
FROM lib_items li
JOIN LIB_ITEMS parent ON li.PARENT_ID=parent.ITEM_ID
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE=lit.TYPE_ID
JOIN USERS created ON li.CREATED_BY=created.USER_ID
JOIN USERS modified ON li.MODIFIED_BY=modified.USER_ID
ORDER BY li.CONTENT_SIZE DESC;
15. Users Consuming Most Space - By Creator
Identifies which users have created the most content, with breakdowns by DXP and SBDF file types and their respective sizes.
SELECT
SUM(li.CONTENT_SIZE)/(1024*1024) AS [TOTAL CONTENT_SIZE (MB)],
SUM(CASE WHEN lit.FILE_SUFFIX='dxp' THEN 1 ELSE 0 END) AS [# DXP FILES],
SUM(CASE WHEN lit.FILE_SUFFIX='dxp' THEN li.CONTENT_SIZE ELSE 0 END)/(1024*1024) AS [DXP FILE SIZE (MB)],
SUM(CASE WHEN lit.FILE_SUFFIX='sbdf' THEN 1 ELSE 0 END) AS [# SBDF FILES],
SUM(CASE WHEN lit.FILE_SUFFIX='sbdf' THEN li.CONTENT_SIZE ELSE 0 END)/(1024*1024) AS [SBDF FILE SIZE (MB)],
created.DISPLAY_NAME AS [CREATED BY]
FROM lib_items li
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE=lit.TYPE_ID
JOIN USERS created ON li.CREATED_BY=created.USER_ID
GROUP BY created.DISPLAY_NAME
ORDER BY SUM(li.CONTENT_SIZE)/(1024*1024) DESC;
16. Users Consuming Most Space - By Last Modifier
Shows which users have been most active in modifying library content, useful for understanding who is driving storage growth.
SELECT
SUM(li.CONTENT_SIZE)/(1024*1024) AS [TOTAL CONTENT_SIZE (MB)],
SUM(CASE WHEN lit.FILE_SUFFIX='dxp' THEN 1 ELSE 0 END) AS [# DXP FILES],
SUM(CASE WHEN lit.FILE_SUFFIX='sbdf' THEN li.CONTENT_SIZE ELSE 0 END)/(1024*1024) AS [SBDF FILE SIZE (MB)],
modified.DISPLAY_NAME AS [LAST MODIFIED BY]
FROM lib_items li
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE=lit.TYPE_ID
JOIN USERS modified ON li.MODIFIED_BY=modified.USER_ID
GROUP BY modified.DISPLAY_NAME
ORDER BY SUM(li.CONTENT_SIZE)/(1024*1024) DESC;
17. Large Content Not Accessed Recently (>10MB, Not Used >1 Year)
Critical query for identifying candidates for retirement - shows large files that have not been accessed in over a year or never accessed.
SELECT
li.CONTENT_SIZE/(1024*1024) AS [CONTENT_SIZE (MB)],
li.ITEM_ID,
li.TITLE,
lit.DISPLAY_NAME AS [ITEM TYPE],
created.DISPLAY_NAME AS [CREATED BY],
li.CREATED AS [CREATED DATE],
li.ACCESSED AS [ACCESSED DATE],
parent.TITLE AS [PARENT TITLE]
FROM lib_items li
JOIN LIB_ITEMS parent ON li.PARENT_ID=parent.ITEM_ID
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE=lit.TYPE_ID
JOIN USERS created ON li.CREATED_BY=created.USER_ID
WHERE (li.ACCESSED IS NULL OR li.ACCESSED < DATEADD(YEAR,-1,GETDATE()))
AND li.CONTENT_SIZE/(1024*1024) > 10
ORDER BY li.CONTENT_SIZE DESC;
18. Spotfire Data Sources Listing (Oracle)
Oracle-specific query to list all Spotfire Data Sources with their connection properties, authentication types, and pool settings. This view extracts data source XML from BLOB storage.
-- Query sample: Get all SQL Server Data Sources SELECT * FROM SPF_DATA_SOURCES_V WHERE DS_TYPE = 'Sqlserver'; -- Useful data source columns: -- DS_ID, DS_NAME, DS_PATH, DS_TYPE, DS_CONN_URL -- DS_CREATED_BY_NAME, DS_MODIFIED_BY_NAME -- DS_AUTHENTICATION, DS_CREDENTIALS_TIMEOUT -- DS_WRITE_ALLOWED, DS_FETCH_SIZE, DS_BATCH_SIZE
Note: This uses SPF_DATA_SOURCES_V view. For the full view definition including XML extraction, see the Spotfire Community article.
19. User Groups Flattened Hierarchy (Oracle)
Shows which users belong to which groups in a flattened hierarchy, including nested group memberships and email addresses for direct contact.
SELECT * FROM SPF_GROUP_USERS_FLAT_V WHERE USER_ENABLED = 1 ORDER BY GROUP_NAME, USER_NAME; -- Returns: GROUP_ID, GROUP_NAME, USER_ID, USER_NAME, -- USER_EMAIL, LAST_LOGIN, USER_ENABLED
20. User Emails by Group (Oracle)
Extracts all user email addresses grouped by Spotfire group, useful for sending communications to specific user groups.
SELECT GROUP_NAME, USERS_LIST_CLOB FROM SPF_USER_EMAILS_BY_GROUP_V ORDER BY GROUP_NAME; -- Returns: GROUP_NAME and USERS_LIST_CLOB -- CLOB field contains semicolon-separated emails -- Ready to copy/paste into Outlook
21. Consolidated User Action Log Events (Oracle)
Provides a consolidated view of action log events across different log categories with readable descriptions of user actions, IP addresses, and item references.
SELECT * FROM SPF_ACTION_LOG_EVENTS_V WHERE EVENT_DATE_TIME >= TRUNC(SYSDATE) - 7 ORDER BY EVENT_DATE_TIME DESC; -- Returns: EVENT_DATE_TIME, IP_ADDRESS, USER_ID, USER_NAME, -- LOG_CATEGORY, LOG_ACTION, LOG_ACTION_DESC, -- TOP_FOLDER_NAME, LIB_ITEM_NAME, PAGE_NAME
22. Update Data Source Passwords (Oracle/SQL Server)
Method for programmatically updating data source connection URLs and passwords after environment cloning. Requires BLOB manipulation and HEX encoding.
-- Step 1: Extract current Data Source BLOB DECLARE @lob_in VARBINARY(MAX); DECLARE @item_id VARCHAR(36) = 'YOUR_DATASOURCE_ITEM_ID'; SELECT @lob_in = [DATA] FROM LIB_DATA WHERE ITEM_ID = @item_id; -- Step 2: Update with new credentials (update the HEX string with your values) UPDATE LIB_DATA SET [DATA] = NEW_HEX_ENCODED_BLOB WHERE ITEM_ID = @item_id;
Note: Data source passwords are encrypted. You must manually set the desired credentials once in a test data source first to obtain the encrypted format.
23. Library Items by Type Summary
Provides a summary count and total size of library items grouped by type (DXP, SBDF, folders, etc.).
SELECT
lit.DISPLAY_NAME AS ITEM_TYPE,
COUNT(*) AS ITEM_COUNT,
SUM(li.CONTENT_SIZE) / (1024*1024.0) AS TOTAL_SIZE_MB,
AVG(li.CONTENT_SIZE) / (1024.0) AS AVG_SIZE_KB,
MIN(li.CREATED) AS EARLIEST_CREATED,
MAX(li.MODIFIED) AS LATEST_MODIFIED
FROM LIB_ITEMS li
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE = lit.TYPE_ID
GROUP BY lit.DISPLAY_NAME, lit.FILE_SUFFIX
ORDER BY TOTAL_SIZE_MB DESC;
24. Most Active Folders by Recent Modification
Identifies folders where the most recent content changes have occurred, helping to focus on actively used areas of the library.
SELECT TOP 25
parent.ITEM_ID,
parent.TITLE AS FOLDER_NAME,
COUNT(li.ITEM_ID) AS CONTENT_COUNT,
MAX(li.MODIFIED) AS LAST_MODIFIED_IN_FOLDER,
MAX(li.ACCESSED) AS LAST_ACCESSED_IN_FOLDER,
SUM(li.CONTENT_SIZE) / (1024*1024.0) AS FOLDER_SIZE_MB
FROM LIB_ITEMS li
JOIN LIB_ITEMS parent ON li.PARENT_ID = parent.ITEM_ID
GROUP BY parent.ITEM_ID, parent.TITLE
ORDER BY MAX(li.MODIFIED) DESC;
25. Content By Creator - Historical Analysis
Shows which users have created content over time, useful for identifying key contributors and ownership patterns.
SELECT
u.DISPLAY_NAME,
u.USER_NAME,
COUNT(*) AS TOTAL_ITEMS_CREATED,
SUM(li.CONTENT_SIZE) / (1024*1024.0) AS TOTAL_SIZE_MB,
MIN(li.CREATED) AS FIRST_CREATED,
MAX(li.CREATED) AS LAST_CREATED
FROM LIB_ITEMS li
JOIN USERS u ON li.CREATED_BY = u.USER_ID
WHERE li.ITEM_TYPE NOT IN (SELECT TYPE_ID FROM LIB_ITEM_TYPES WHERE DISPLAY_NAME = 'folder')
GROUP BY u.DISPLAY_NAME, u.USER_NAME
ORDER BY TOTAL_SIZE_MB DESC;
26. Recently Modified Items (Last 7 Days)
Tracks recent changes in the library to monitor active development and ongoing maintenance activities.
SELECT
li.ITEM_ID,
li.TITLE,
lit.DISPLAY_NAME AS ITEM_TYPE,
u.DISPLAY_NAME AS MODIFIED_BY,
li.MODIFIED,
li.CONTENT_SIZE / (1024*1024.0) AS SIZE_MB,
parent.TITLE AS PARENT_FOLDER
FROM LIB_ITEMS li
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE = lit.TYPE_ID
JOIN USERS u ON li.MODIFIED_BY = u.USER_ID
LEFT JOIN LIB_ITEMS parent ON li.PARENT_ID = parent.ITEM_ID
WHERE li.MODIFIED >= DATEADD(DAY, -7, GETDATE())
ORDER BY li.MODIFIED DESC;
27. Object Owner Attribution - Complete Inventory
Maps all library objects to their creators with comprehensive metadata for ownership tracking and governance.
SELECT
u.USER_NAME,
u.DISPLAY_NAME,
COUNT(DISTINCT li.ITEM_ID) AS OBJECT_COUNT,
SUM(li.CONTENT_SIZE) / (1024*1024.0) AS OWNER_TOTAL_MB,
MIN(li.CREATED) AS OLDEST_OBJECT,
MAX(li.MODIFIED) AS LATEST_MODIFICATION
FROM LIB_ITEMS li
JOIN USERS u ON li.CREATED_BY = u.USER_ID
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE = lit.TYPE_ID
WHERE lit.DISPLAY_NAME NOT IN ('Folder')
GROUP BY u.USER_ID, u.USER_NAME, u.DISPLAY_NAME
ORDER BY OWNER_TOTAL_MB DESC;
28. Orphaned Items Detection
Identifies library items with missing parent folders or invalid references.
SELECT
li.ITEM_ID,
li.TITLE,
lit.DISPLAY_NAME AS ITEM_TYPE,
li.PARENT_ID,
li.CONTENT_SIZE / (1024*1024.0) AS SIZE_MB
FROM LIB_ITEMS li
LEFT JOIN LIB_ITEMS parent ON li.PARENT_ID = parent.ITEM_ID
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE = lit.TYPE_ID
WHERE li.PARENT_ID IS NOT NULL
AND parent.ITEM_ID IS NULL
ORDER BY li.CONTENT_SIZE DESC;
29. Nested Folder Depth Analysis
Analyzes folder hierarchy depth to identify overly deep structures affecting usability.
WITH folder_hierarchy AS (
SELECT ITEM_ID, PARENT_ID, TITLE, 1 AS depth
FROM LIB_ITEMS
WHERE ITEM_TYPE = (SELECT TYPE_ID FROM LIB_ITEM_TYPES WHERE DISPLAY_NAME = 'Folder')
AND PARENT_ID IS NULL
UNION ALL
SELECT li.ITEM_ID, li.PARENT_ID, li.TITLE, fh.depth + 1
FROM LIB_ITEMS li
INNER JOIN folder_hierarchy fh ON li.PARENT_ID = fh.ITEM_ID
)
SELECT depth, COUNT(*) AS folder_count
FROM folder_hierarchy
GROUP BY depth
ORDER BY depth DESC;
30. DXP Files with Embedded Data Size
Lists DXP analyses and shows which ones have embedded data (larger file size), useful for optimizing storage.
SELECT
li.ITEM_ID,
li.TITLE,
li.CONTENT_SIZE / (1024*1024.0) AS DXP_SIZE_MB,
CASE WHEN li.CONTENT_SIZE > (1024*1024*5) THEN 'Likely Embedded Data' ELSE 'Small' END AS DATA_STATUS,
u.DISPLAY_NAME AS CREATED_BY,
li.CREATED,
parent.TITLE AS PARENT_FOLDER
FROM LIB_ITEMS li
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE = lit.TYPE_ID
JOIN USERS u ON li.CREATED_BY = u.USER_ID
LEFT JOIN LIB_ITEMS parent ON li.PARENT_ID = parent.ITEM_ID
WHERE lit.FILE_SUFFIX = 'dxp'
ORDER BY li.CONTENT_SIZE DESC;
31. SBDF Files Inventory and Location
Lists all SBDF (Spotfire Binary Data Files) with their sizes, creators, and locations.
SELECT
li.ITEM_ID,
li.TITLE,
li.CONTENT_SIZE / (1024*1024.0) AS SBDF_SIZE_MB,
u.DISPLAY_NAME AS CREATED_BY,
li.CREATED,
li.ACCESSED,
DATEDIFF(DAY, li.ACCESSED, GETDATE()) AS DAYS_SINCE_ACCESS,
parent.TITLE AS PARENT_FOLDER
FROM LIB_ITEMS li
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE = lit.TYPE_ID
JOIN USERS u ON li.CREATED_BY = u.USER_ID
LEFT JOIN LIB_ITEMS parent ON li.PARENT_ID = parent.ITEM_ID
WHERE lit.FILE_SUFFIX = 'sbdf'
ORDER BY li.CONTENT_SIZE DESC;
32. Disabled User Accounts with Active Content
Identifies content created or owned by disabled user accounts, useful for access cleanup.
SELECT
u.USER_NAME,
u.DISPLAY_NAME,
u.ENABLED,
COUNT(DISTINCT li.ITEM_ID) AS CONTENT_COUNT,
SUM(li.CONTENT_SIZE) / (1024*1024.0) AS TOTAL_SIZE_MB,
MAX(li.MODIFIED) AS LAST_MODIFIED_CONTENT
FROM LIB_ITEMS li
JOIN USERS u ON li.CREATED_BY = u.USER_ID
WHERE u.ENABLED = 0
GROUP BY u.USER_ID, u.USER_NAME, u.DISPLAY_NAME, u.ENABLED
ORDER BY TOTAL_SIZE_MB DESC;
33. Group Membership Analysis Over Time
Shows how many users are in each group, useful for load distribution and governance.
SELECT
g.GROUP_NAME,
COUNT(DISTINCT gm.MEMBER_USER_ID) AS ACTIVE_USERS,
COUNT(DISTINCT gm.MEMBER_GROUP_ID) AS NESTED_GROUPS,
g.CONNECTED AS LDAP_SYNCED,
MIN(u.LAST_LOGIN) AS EARLIEST_LOGIN,
MAX(u.LAST_LOGIN) AS LATEST_LOGIN
FROM GROUPS g
LEFT JOIN GROUP_MEMBERS gm ON g.GROUP_ID = gm.GROUP_ID
LEFT JOIN USERS u ON gm.MEMBER_USER_ID = u.USER_ID
GROUP BY g.GROUP_ID, g.GROUP_NAME, g.CONNECTED
HAVING COUNT(DISTINCT gm.MEMBER_USER_ID) > 0
ORDER BY ACTIVE_USERS DESC;
34. Permission Inconsistencies - Inherited vs Direct
Identifies folders with both inherited and direct permissions assigned.
SELECT
li.ITEM_ID,
li.TITLE,
COUNT(DISTINCT la.USER_ID) AS DIRECT_USER_PERMS,
COUNT(DISTINCT la.GROUP_ID) AS DIRECT_GROUP_PERMS,
CASE WHEN li.PARENT_ID IS NOT NULL THEN 'Has Parent (Inheritance Possible)' ELSE 'Root Item' END AS INHERITANCE_STATUS
FROM LIB_ITEMS li
LEFT JOIN LIB_ACCESS la ON li.ITEM_ID = la.ITEM_ID
GROUP BY li.ITEM_ID, li.TITLE, li.PARENT_ID
HAVING COUNT(DISTINCT la.USER_ID) + COUNT(DISTINCT la.GROUP_ID) > 0
ORDER BY DIRECT_USER_PERMS + DIRECT_GROUP_PERMS DESC;
35. Users with Multiple Login Sessions
Identifies users with many logins, useful for understanding engagement and anomaly detection.
SELECT
USER_NAME,
COUNT(*) AS LOGIN_COUNT,
MIN(LOGGED_TIME) AS FIRST_LOGIN,
MAX(LOGGED_TIME) AS LATEST_LOGIN,
DATEDIFF(DAY, MIN(LOGGED_TIME), MAX(LOGGED_TIME)) AS DAYS_ACTIVE
FROM ACTIONLOG
WHERE LOG_CATEGORY = 'auth'
AND LOG_ACTION = 'login'
AND SUCCESS = 1
GROUP BY USER_NAME
HAVING COUNT(*) > 10
ORDER BY LOGIN_COUNT DESC;
36. Failed Login Attempts by User
Security-focused query to identify possible intrusion attempts or account access issues.
SELECT
USER_NAME,
COUNT(*) AS FAILED_ATTEMPTS,
MIN(LOGGED_TIME) AS FIRST_ATTEMPT,
MAX(LOGGED_TIME) AS LAST_ATTEMPT
FROM ACTIONLOG
WHERE LOG_CATEGORY IN ('auth', 'auth_wp', 'auth_pro')
AND LOG_ACTION = 'login'
AND SUCCESS = 0
GROUP BY USER_NAME
HAVING COUNT(*) > 3
ORDER BY FAILED_ATTEMPTS DESC;
37. Content Access by Permission Level
Shows distribution of content across different permission levels (Browse, Modify, Full Control).
SELECT
CASE
WHEN PERMISSION = 'X' THEN 'Access Only'
WHEN PERMISSION = 'R' THEN 'Browse'
WHEN PERMISSION = 'W' THEN 'Modify'
WHEN PERMISSION = 'O' THEN 'Full Control'
ELSE 'Unknown'
END AS PERMISSION_LEVEL,
COUNT(DISTINCT ITEM_ID) AS ITEMS_WITH_PERM,
COUNT(DISTINCT USER_ID) + COUNT(DISTINCT GROUP_ID) AS PRINCIPALS_GRANTED
FROM LIB_ACCESS
GROUP BY PERMISSION
ORDER BY COUNT(*) DESC;
38. Data Source Usage in Analyses
Counts how many analyses reference each data source, useful for impact analysis.
SELECT
ds.TITLE AS DATA_SOURCE_NAME,
COUNT(DISTINCT li.ITEM_ID) AS ANALYSIS_COUNT,
STRING_AGG(DISTINCT li.TITLE, ', ') AS REFERENCING_ANALYSES
FROM LIB_ITEMS ds
CROSS APPLY (
SELECT li.ITEM_ID, li.TITLE
FROM LIB_ITEMS li
WHERE li.ITEM_TYPE = (SELECT TYPE_ID FROM LIB_ITEM_TYPES WHERE DISPLAY_NAME = 'dxp')
) AS li
WHERE ds.TITLE LIKE '%Data%' OR ds.TITLE LIKE '%Source%'
GROUP BY ds.ITEM_ID, ds.TITLE
ORDER BY ANALYSIS_COUNT DESC;
39. Content Distribution by File Type
Shows the breakdown of storage and item count by file type (.dxp, .sbdf, folders, etc.).
SELECT
lit.FILE_SUFFIX,
lit.DISPLAY_NAME,
COUNT(*) AS ITEM_COUNT,
SUM(li.CONTENT_SIZE) / (1024*1024*1024.0) AS TOTAL_SIZE_GB,
AVG(li.CONTENT_SIZE) / (1024*1024.0) AS AVG_SIZE_MB,
MIN(li.CREATED) AS OLDEST_ITEM,
MAX(li.MODIFIED) AS NEWEST_MODIFICATION
FROM LIB_ITEMS li
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE = lit.TYPE_ID
GROUP BY lit.FILE_SUFFIX, lit.DISPLAY_NAME, lit.TYPE_ID
ORDER BY TOTAL_SIZE_GB DESC;
40. Web Player vs Client Usage Analysis
Compares usage patterns between Web Player and client (thick client) logins.
SELECT
CASE
WHEN LOG_CATEGORY LIKE '%_wp' THEN 'Web Player'
WHEN LOG_CATEGORY LIKE '%_pro' THEN 'Desktop Client'
ELSE 'Other'
END AS CLIENT_TYPE,
LOG_ACTION,
COUNT(*) AS EVENT_COUNT,
COUNT(DISTINCT USER_NAME) AS UNIQUE_USERS,
MIN(LOGGED_TIME) AS FIRST_EVENT,
MAX(LOGGED_TIME) AS LAST_EVENT
FROM ACTIONLOG
WHERE LOG_CATEGORY IN ('auth_wp', 'auth_pro', 'library_wp', 'file_pro')
GROUP BY LOG_CATEGORY, LOG_ACTION
ORDER BY CLIENT_TYPE, EVENT_COUNT DESC;
41. Peak Usage Hours Analysis
Identifies peak usage times by analyzing login and activity timestamps for capacity planning.
SELECT
DATEPART(HOUR, LOGGED_TIME) AS HOUR_OF_DAY,
DATEPART(WEEKDAY, LOGGED_TIME) AS DAY_OF_WEEK,
COUNT(*) AS EVENT_COUNT,
COUNT(DISTINCT USER_NAME) AS UNIQUE_USERS
FROM ACTIONLOG
WHERE LOG_CATEGORY IN ('auth_wp', 'auth_pro')
AND LOG_ACTION = 'login'
AND SUCCESS = 1
GROUP BY DATEPART(HOUR, LOGGED_TIME), DATEPART(WEEKDAY, LOGGED_TIME)
ORDER BY EVENT_COUNT DESC;
42. Scheduled Report Refresh Dependencies
Maps scheduled analyses and their data source dependencies.
SELECT
li.ITEM_ID,
li.TITLE,
li.CREATED,
u.DISPLAY_NAME AS CREATED_BY,
CASE WHEN li.CONTENT_SIZE > (1024*1024*10) THEN 'Large' ELSE 'Normal' END AS SIZE_CATEGORY,
COUNT(DISTINCT ds.ITEM_ID) AS DATA_SOURCE_COUNT
FROM LIB_ITEMS li
LEFT JOIN LIB_ITEMS ds ON li.PARENT_ID = ds.ITEM_ID
JOIN USERS u ON li.CREATED_BY = u.USER_ID
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE = lit.TYPE_ID
WHERE lit.DISPLAY_NAME = 'dxp'
GROUP BY li.ITEM_ID, li.TITLE, li.CREATED, u.DISPLAY_NAME, li.CONTENT_SIZE
HAVING COUNT(DISTINCT ds.ITEM_ID) > 0
ORDER BY DATA_SOURCE_COUNT DESC;
43. User Inactivity Detection
Identifies users who have not accessed Spotfire in a specified period for license optimization.
SELECT
u.USER_NAME,
u.DISPLAY_NAME,
u.LAST_LOGIN,
DATEDIFF(DAY, u.LAST_LOGIN, GETDATE()) AS DAYS_INACTIVE,
CASE
WHEN DATEDIFF(DAY, u.LAST_LOGIN, GETDATE()) > 180 THEN 'Inactive (6+ months)'
WHEN DATEDIFF(DAY, u.LAST_LOGIN, GETDATE()) > 90 THEN 'Inactive (3+ months)'
WHEN DATEDIFF(DAY, u.LAST_LOGIN, GETDATE()) > 30 THEN 'Inactive (1+ month)'
ELSE 'Active'
END AS ACTIVITY_STATUS
FROM USERS u
WHERE u.ENABLED = 1
AND u.LAST_LOGIN < DATEADD(DAY, -30, GETDATE())
ORDER BY LAST_LOGIN ASC;
44. Object Edit History Summary
Shows which users have edited which objects most frequently for change tracking.
SELECT
li.ITEM_ID,
li.TITLE,
u.DISPLAY_NAME AS LAST_MODIFIED_BY,
li.MODIFIED AS LAST_MODIFICATION_DATE,
DATEDIFF(DAY, li.MODIFIED, GETDATE()) AS DAYS_SINCE_MODIFICATION,
lit.DISPLAY_NAME AS ITEM_TYPE
FROM LIB_ITEMS li
JOIN USERS u ON li.MODIFIED_BY = u.USER_ID
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE = lit.TYPE_ID
WHERE li.ITEM_TYPE IN (SELECT TYPE_ID FROM LIB_ITEM_TYPES WHERE FILE_SUFFIX IN ('dxp', 'sbdf'))
ORDER BY li.MODIFIED DESC;
45. Content Classification by Age
Segments library content by creation date for maintenance and archival planning.
SELECT
CASE
WHEN DATEDIFF(MONTH, li.CREATED, GETDATE()) < 3 THEN 'New (< 3 months)'
WHEN DATEDIFF(MONTH, li.CREATED, GETDATE()) < 12 THEN 'Recent (3-12 months)'
WHEN DATEDIFF(MONTH, li.CREATED, GETDATE()) < 24 THEN 'Mature (1-2 years)'
ELSE 'Legacy (2+ years)'
END AS AGE_CATEGORY,
COUNT(*) AS ITEM_COUNT,
SUM(li.CONTENT_SIZE) / (1024*1024.0) AS TOTAL_SIZE_MB,
AVG(li.CONTENT_SIZE) / (1024.0) AS AVG_SIZE_KB
FROM LIB_ITEMS li
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE = lit.TYPE_ID
WHERE lit.FILE_SUFFIX IN ('dxp', 'sbdf')
GROUP BY CASE
WHEN DATEDIFF(MONTH, li.CREATED, GETDATE()) < 3 THEN 'New (< 3 months)'
WHEN DATEDIFF(MONTH, li.CREATED, GETDATE()) < 12 THEN 'Recent (3-12 months)'
WHEN DATEDIFF(MONTH, li.CREATED, GETDATE()) < 24 THEN 'Mature (1-2 years)'
ELSE 'Legacy (2+ years)'
END
ORDER BY CASE
WHEN DATEDIFF(MONTH, li.CREATED, GETDATE()) < 3 THEN 1
WHEN DATEDIFF(MONTH, li.CREATED, GETDATE()) < 12 THEN 2
WHEN DATEDIFF(MONTH, li.CREATED, GETDATE()) < 24 THEN 3
ELSE 4
END;
46. Top Contributors by Output Volume
Identifies power users who create the most analyses and content by volume and activity.
SELECT TOP 20
u.USER_NAME,
u.DISPLAY_NAME,
COUNT(DISTINCT li.ITEM_ID) AS ANALYSES_CREATED,
SUM(li.CONTENT_SIZE) / (1024*1024.0) AS TOTAL_SIZE_MB,
MIN(li.CREATED) AS FIRST_CREATION,
MAX(li.MODIFIED) AS LAST_MODIFICATION,
DATEDIFF(DAY, MIN(li.CREATED), MAX(li.MODIFIED)) AS ACTIVE_SPAN_DAYS
FROM LIB_ITEMS li
JOIN USERS u ON li.CREATED_BY = u.USER_ID
JOIN LIB_ITEM_TYPES lit ON li.ITEM_TYPE = lit.TYPE_ID
WHERE lit.FILE_SUFFIX IN ('dxp', 'sbdf')
GROUP BY u.USER_ID, u.USER_NAME, u.DISPLAY_NAME
ORDER BY COUNT(DISTINCT li.ITEM_ID) DESC;
Summary and Additional Resources
This comprehensive collection of 46 SQL queries provides Spotfire administrators with powerful tools to:
- Inventory Management: Track library content, sizes, and growth patterns
- Storage Optimization: Identify large and unused items for retirement or archival
- User & Group Analysis: Audit access patterns, permissions, and group memberships
- Activity Monitoring: Analyze usage patterns and user behavior via action logs
- Data Source Management: List and update data source configurations across environments
- Governance & Compliance: Generate reports for regulatory and internal audits
Database-Specific Notes:
- SQL Server: Queries 1-17 use SQL Server syntax (DATEADD, GETDATE(), etc.)
- Oracle: Queries 18-21 use Oracle-specific views and syntax (TRUNC, FROM_TZ, LISTAGG, etc.)
- PostgreSQL: May require syntax adjustments for date functions and aggregate functions
Key Resources Referenced:
- TIBCO Support: Example SQL Queries for Library Content Size
- Spotfire Community: Spotfire Metadata Queries
- Spotfire Server Documentation on Action Logs and Metadata
Final Recommendations:
- Always test queries in development/non-production environments first
- Use read-only database accounts when possible for querying sensitive metadata
- Schedule regular execution of monitoring queries for trend analysis
- Build Spotfire analyses using Information Links to these queries for interactive dashboards
- Document query execution dates and baseline values for governance compliance
- Archive historical results to track library growth and changes over time
Happy Spotfire administrating!
Disclaimer & Attribution
Disclaimer: The SQL queries provided in this post are compiled from publicly available sources, including TIBCO documentation, community contributions, and industry best practices. I do not claim original ownership of all these queries. Many of these queries have been adapted, modified, or sourced from:
- TIBCO Support: Example SQL Queries for managing Spotfire library content (KB0072647)
- Spotfire Community: User-contributed metadata queries and administration scripts
- Spotfire Server Documentation: Official TIBCO documentation on metadata and action logs
- Industry Standard Practices: Common SQL patterns for BI administration and governance
Purpose of This Post: This blog consolidates these queries in one comprehensive reference guide for Spotfire administrators. The queries are provided as-is for educational and administrative purposes in non-production test environments.
Terms of Use:
- Always test queries in development/non-production environments first
- Modify queries according to your specific Spotfire version and database schema
- Use read-only database accounts when executing metadata queries
- Refer to official TIBCO documentation for your specific Spotfire version
- No warranty is provided for these queries - use at your own risk
Attribution & References:
- TIBCO Support: Example SQL Queries for Library Content Size
- Spotfire Community: Spotfire Metadata Queries
- TIBCO Spotfire Official Documentation
- Thanks to the Spotfire community for sharing knowledge and best practices
Last Updated: December 2025 | Content for Spotfire Server 7.x through 14.x




