SQL Server 2012 Metadata Syntax - sql-server

Sorry for the dumb question in advance and thanks for your time!
I have two queries
Query A
SELECT sc.name +'.'+ta.name TableName
,SUM(pa.rows) RowCnt
FROM sys.tables ta
INNER JOIN sys.partitions pa
ON pa.object_id = ta.object_id
INNER JOIN sys.schemas sc
ON ta.schema_id=sc.schema_id
WHERE ta.is_ms_shipped = 0 AND pa.index_id IN (1,0)
GROUP BY sc.name,ta.name
ORDER BY sum(pa.rows) DESC;
and
Query B
SELECT ta.name TableName
, ta.modify_date ModDate
, ta.max_column_id_used "Columns"
FROM sys.tables ta
ORDER BY ta.name;
which execute fine.
When I add the lines
, ta.modify_date ModDate
, ta.max_column_id_used "Columns"
I create Query C
SELECT sc.name +'.'+ta.name TableName
,SUM(pa.rows) RowCnt
, ta.modify_date ModDate
, ta.max_column_id_used "Columns"
FROM sys.tables ta
INNER JOIN sys.partitions pa
ON pa.object_id = ta.object_id
INNER JOIN sys.schemas sc
ON ta.schema_id=sc.schema_id
WHERE ta.is_ms_shipped = 0 AND pa.index_id IN (1,0)
GROUP BY sc.name,ta.name
ORDER BY sum(pa.rows) DESC;
I get the error
8120, Level 16, State 1, Line 3
Column 'sys.tables.modify_date' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I understand what aggregate fx are as well as the purpose of GROUP BY.
Do I need to create a subquery to include this information in one query

Related

Join INFORMATION_SCHEMA.TABLES with another table with more TABLE_NAMES and result would be different if table name is in the first table or not

we have a Table with a list of table names we want to be created. They don't have an ID column or anything, it's just a few rows of data with 2 columns. Thing is we want to merge that table with Information_schema.table to check which of the tables we have already created and which we have not, so we wrote the query below as a temp to achieve such:
with cte1 as (
select d.TABNAME, d.CLASS from dbo.table_list as d
left join INFORMATION_SCHEMA.TABLES as t on t.TABLE_NAME = d.TABNAME
where d.CLASS in ('INIT','STERN') and table_schema = 'dbo'),
cte2 as (select d.TABNAME, d.CLASS
from dbo.table_list as d
where d.CLASS in ('INIT','TERN') and d.TABNAME not in (select [TABLE NAME] from cte1))
select *, 'Active' as [Status] from cte1 union all
select * , 'Inactive' from cte2
This is what table_list looks like:
TABNAME
CLASS
TABLE1
INIT
TABLE2
STERN
TABLE3
STERN
TABLE4
STERN
TABLE5
INIT
We already have TABLE1 and TABLE2 created so the result of the query looks like this:
TABNAME
CLASS
STATUS
TABLE1
INIT
Active
TABLE2
STERN
Active
TABLE3
STERN
Inactive
TABLE4
STERN
Inactive
TABLE5
INIT
Inactive
It works well enough like this but we were wondering if we could make it shorter.
This can be way shorter, yes. You could just reference the table dbo.table_list and see if you get a valid OBJECT_ID:
SELECT tl.TABNAME,
tl.CLASS,
CASE WHEN OBJECT_ID(N'dbo.' + QUOTENAME(tl.TABNAME)) IS NULL THEN 'Inactive' ELSE 'Active' END AS Status
FROM dbo.table_list tl --"d" for "table_list" doesn't make a lot of sense.
WHERE tl.CLASS IN ('INIT','STERN');
If you wanted to use the catalog views, you could use CROSS APPLY to join to the table while supplying a value for both the schema and table name, or just JOIN to sys.schemas based on a literal and then LEFT JOIN to sys.tables:
SELECT tl.TABNAME,
tl.CLASS,
CASE WHEN st.[name] IS NULL THEN 'Inactive' ELSE 'Active' END AS Status
FROM dbo.table_list tl --"d" for "table_list" doesn't make a lot of sense.
CROSS APPLY (SELECT t.[name]
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
WHERE s.[name] = N'dbo'
AND t.[name] = tl.TABNAME) st
WHERE tl.CLASS IN ('INIT','STERN');
SELECT tl.TABNAME,
tl.CLASS,
CASE WHEN t.[name] IS NULL THEN 'Inactive' ELSE 'Active' END AS Status
FROM dbo.table_list tl --"d" for "table_list" doesn't make a lot of sense.
JOIN sys.schemas s ON s.[name] = N'dbo'
LEFT JOIN sys.tables t ON s.schema_id = t.schema_id
AND tl.TABNAME = t.[name]
WHERE tl.CLASS IN ('INIT','STERN');

Putting many tables in to one table in SQL Server?

I'm trying to insert the result from a query into a new table.
I'm using this query and want to gather the result into a single table.
The query (I found somewhere) looks like this:
USE [AdventureWorksDW2012]
SELECT
OBJECT_SCHEMA_NAME(T.[object_id],DB_ID()) AS [Schema],
T.[name] AS [table_name],
AC.[name] AS [column_name],
TY.[name] AS system_data_type,
AC.[max_length],
AC.[precision], AC.[scale],
AC.[is_nullable], AC.[is_ansi_padded]
FROM
sys.[tables] AS T
INNER JOIN
sys.[all_columns] AC ON T.[object_id] = AC.[object_id]
INNER JOIN
sys.[types] TY ON AC.[system_type_id] = TY.[system_type_id]
AND AC.[user_type_id] = TY.[user_type_id]
WHERE
T.[is_ms_shipped] = 0
ORDER BY
T.[name], AC.[column_id];
Try using an INTO clause like this:
USE [AdventureWorksDW2012]
SELECT OBJECT_SCHEMA_NAME(T.[object_id],DB_ID()) AS [Schema],
T.[name] AS [table_name], AC.[name] AS [column_name],
TY.[name] AS system_data_type, AC.[max_length],
AC.[precision], AC.[scale], AC.[is_nullable], AC.[is_ansi_padded]
INTO dbo.MyNewTable
FROM sys.[tables] AS T
INNER JOIN sys.[all_columns] AC ON T.[object_id] = AC.[object_id]
INNER JOIN sys.[types] TY ON AC.[system_type_id] = TY.[system_type_id]
AND AC.[user_type_id] = TY.[user_type_id]
WHERE T.[is_ms_shipped] = 0
ORDER BY T.[name], AC.[column_id]
;
Use INTO clause as next:-
USE [AdventureWorksDW2012]
SELECT OBJECT_SCHEMA_NAME(T.[object_id],DB_ID()) AS [Schema],
T.[name] AS [table_name], AC.[name] AS [column_name],
TY.[name] AS system_data_type, AC.[max_length],
AC.[precision], AC.[scale], AC.[is_nullable], AC.[is_ansi_padded]
Into New_table -- this line is added.
FROM sys.[tables] AS T
INNER JOIN sys.[all_columns] AC ON T.[object_id] = AC.[object_id]
INNER JOIN sys.[types] TY ON AC.[system_type_id] = TY.[system_type_id] AND AC.[user_type_id] = TY.[user_type_id]
WHERE T.[is_ms_shipped] = 0
ORDER BY T.[name], AC.[column_id]
The sample code is
Select *
Into New_table
From Exist_Table
and as MSDN says:-
SELECT…INTO creates a new table in the default filegroup and inserts
the resulting rows from the query into it.

SQLServer reference outer query

Is it possible to reference an outer query from an inner query in the context of a join? The "where (sid.ItemID = i.itemID)" of the inner query is giving me an error. I thought I did this in the past so I search through all my stored procs but apparently I did some kind of substitution to get it to work. I suspect I can delete that line and it will work but is it more effecient with that inner where clause?
SELECT departmentName
, supplierName
, so.SalesOrderID
, ss.warehouseInvoiceNo
, ss.transactionNo
, ss.storeID
, s.storeName
, s.storeNo
, tr.transactionDate
, p.period
, sooos.salesOrderID
, sooos.salesOrderOutOfStockID
, sooos.itemID
, i.itemNo
, i.itemName
, i.pack
, i.unitSize
, quantity
, wi.available
FROM SalesOrderOutOfStock sooos
JOIN Item AS i ON i.ItemID = sooos.ItemID
JOIN SalesOrder so ON so.SalesOrderID = sooos.SalesOrderID
JOIN WarehouseInventory wi ON wi.ItemID = sooos.ItemID
JOIN Store s ON s.StoreID = so.StoreID
JOIN InvoiceOrderRelationship ior ON ior.SalesOrderID = so.SalesOrderID
JOIN StockSale ss ON ss.WarehouseInvoiceNo = ior.WarehouseInvoiceNo
JOIN TransactionRegister tr ON tr.TransactionNo = ss.TransactionNo
JOIN Period p ON p.PeriodID = tr.PeriodID
JOIN Department d ON d.DepartmentID = i.DepartmentID
LEFT OUTER JOIN (SELECT TOP 1 itemID
, supplierID
FROM SupplierInvoiceDetail sid
JOIN SupplierInvoice si ON si.SupplierInvoiceID = sid.SupplierInvoiceID
--where (sid.ItemID = i.itemID)
order by InvoiceDate desc
--NEED AN ORDER BY HERE
) AS lastSupplier ON lastSupplier.ItemID = i.ItemID
JOIN supplier su ON su.SupplierID = Isnull(lastSupplier.supplierID, i.supplierID)
WHERE ss.WarehouseInvoiceNo = 10000000
--$P{invoiceNo}
You need to use OUTER APPLY here rather than LEFT JOIN:
OUTER APPLY (SELECT TOP 1 itemID
, supplierID
FROM SupplierInvoiceDetail sid
JOIN SupplierInvoice si ON si.SupplierInvoiceID = sid.SupplierInvoiceID
where (sid.ItemID = i.itemID) order by InvoiceDate desc
) AS lastSupplier
Subqueries introduced in FROM or JOIN clauses cannot refer to other table sources within the same FROM clause (effectively, they should all be evaluatable simultaneously). APPLY allows you to introduce a specific dependency in the evaluation.

Query to return distinct values in fields containing string across multiple tables

The following query looks for fields within a db that contain '%string%' and returns them in a table with 5 columns; Schema, Table, Number of fields containing desired string in title of field within table, number of rows in table, and finally the field names.
SELECT s.name schemaName
, t.name tabName
, COUNT(c.name) OVER (PARTITION BY t.name ORDER BY t.name) totalColsWithString
, rc.row_count
, c.name colName
FROM sys.all_columns c
JOIN sys.tables t ON (t.object_id = c.object_id)
JOIN sys.schemas s ON (s.schema_id = t.schema_id)
LEFT JOIN
(
SELECT o.name
, ddps.row_count
FROM sys.indexes i
JOIN sys.objects o ON (i.object_id = o.object_id)
JOIN sys.dm_db_partition_stats AS ddps ON (i.object_id = ddps.object_id AND i.index_id = ddps.index_id)
WHERE i.index_id < 2 AND o.is_ms_shipped = 0
) rc ON (rc.name = t.name)
WHERE c.name LIKE '%String%'
AND row_count <> 0;
What I now want is a field that shows the number of distinct values in those fields which contain 'string' in the title (in all the columns returned in above query).
Does MS SQL Server store any info about distinct values in fields? Can it be made to?
Updated answer
You actually should consider to run 2 queries in that case. Or use subqueries, which may cause performance issues at a big dataset. Subqueries could look like this
SELECT [...],
(SELECT COUNT(*) FROM all_columns c2 WHERE c2.name= c.name) AS totalcolswithstring
FROM [...]
I set up a fiddle for you SqlFiddle

How can we check whether data exists or not in a table thru sys. tables/functions directly

Is there any way to check whether data exists in a table thru sys. tables or functions directly without querying the table.
Any such sys. available?
** Not querying the dynamic sql..:)
Read this article:
Find Row Count in Table – Find Largest Table in Database
Here is a query to find a ROWCOUNT of a table:
SELECT SUM(pa.rows) RowCnt
FROM sys.tables ta
INNER JOIN sys.partitions pa
ON pa.OBJECT_ID = ta.OBJECT_ID
WHERE ta.is_ms_shipped = 0
AND pa.index_id IN (1,0)
AND ta.name='table1'
SQLFiddle demo
Or if you need only information about empty table or not then something like this:
SELECT
ISNULL(
(SELECT TOP 1 1 from sys.partitions pa
where pa.OBJECT_ID = ta.OBJECT_ID
AND
pa.rows>0
AND
pa.index_id IN (1,0)
)
,0) as TableIsNotEmpty
FROM sys.tables ta
WHERE ta.is_ms_shipped = 0
AND ta.name='table1'
-- This is how I got the result...
SELECT Distinct tbl.name, C.name , X.rowcnt
FROM Sys.Columns as c
INNER JOIN Sys.Tables as tbl
ON tbl.object_id = c.object_id
INNER JOIN Sys.Types as t
ON c.system_type_id = t.system_type_id
INNER JOIN Sys.Indexes I
ON I.object_id = Tbl.object_id
Inner Join Sys.sysindexes X
On I.index_id = X.indid
And I.object_id = X.id
WHERE X.rowcnt > 0
ORDER BY tbl.name

Resources