In TSQL openquery code talking to AS400, how does 'select top 1000 *' interact with "fetch first 1100 row only" - ssms

I have a query submitted from SSMS 18.12.1, talking to SQL Server 11.0.5058.0, and passing through to AS400 using openquery. I was expecting 1000 records, but 1100 records were returned. Why were 1100 records returned?
select top 1000 *
from openquery
( CWDPROD
, 'select *
from collpdata.cetdohc
fetch first 1100 row only
for read only
with ur
') a
Edit: It works if I add 'order by', which seems weird
select top 1000 *
from openquery
( CWDPROD
, 'select *
from collpdata.cetdohc
fetch first 1100 row only
for read only
with ur
') a
order by ohcomp

Related

Create function/procedure to return table to get the last rows

I have several tables, each with a different key.
For example: the key for the Customer could be 2 or more columns.
Input - dbo.customer:
Customer e_Date Value
------------------------
1000 2019-05-26 200
1000 2019-05-25 100
2000 2019-04-23 50
2000 2019-04-21 20
Output :
Customer e_date value
----------------------------
1000 2019-05-26 200
2000 2019-04-23 50
The max dates and the values for them was return for each customer (key).
I want to build a function or procedure in SQL where I will enter the name of table and the key and will return me the output. A return table function.
exec procedure get_Last_Row_By_Key (#Table_Name, #Key)
and it will show me the output.
In this example :
exec procedure get_Last_Row_By_Key ('dbo.customer', Customer)
I guess that when the #key will be multiple value I can do concat of the other columns to make them a one column key.
put below query in your function using row_number() window function
select customer,e_date,value from
(select *,row_number()over(partition by customer order by e_date desc) rn
from table ) a where a.rn=1
The function call would need to be something like this:
exec procedure get_Last_Row_By_Key ('dbo.customer', 'Customer', 'e_date')
To support multiple keys, I would use row_number(), even though that adds an extra column to the result set. So the dynamic SQL would look like:
declare #sql nvarchar(max);
set #sql = '
select t.*
from (select t.*,
row_number() over (partition by [key] order by [datecol]) as seqnum
from [table] t
) t
where seqnum = 1
';
set #sql = replace(#sql, '[table]', #table);
set #sql = replace(#sql, '[key]', #key);
set #sql = replace(#sql, '[datecol]', #datecol);
exec sp_executesql #sql;
Note: I am explicitly not using quotename() here, so the code will allow multiple columns for either the key order "datecol".
Also, as an exercise, this might be useful for learning about dynamic SQL and stored procedures. In general though, such attempts at "generic" processing are not as useful as they seem. People who know SQL know how to write the query to do what they want on a table; they do not know custom stored procedures that do the same thing.

Getting only the length of returned string as result of an SQL query using Derby DB

I want to test whether a length of a string returned from an SQL query is equal to 3. I'm using DERBY DB.
I've tried something like:
ij> select * from tmpuser.friends where length(select distinct ss.schemaname from sys.sysschemas ss fetch next 1 row only)=3;
But this query is invalid. I want to show the result of
select * from tmpuser.friends
only if
length(select distinct ss.schemaname from sys.sysschemas ss fetch next 1 row only)=3;
is true.
ij> select distinct ss.schemaname from sys.sysschemas ss;
SCHEMANAME
--------------------------------------------------------------------------------------------------------------------------------
APP
NULLID
SQLJ
SYS
SYSCAT
SYSCS_DIAG
SYSCS_UTIL
SYSFUN
SYSIBM
SYSPROC
SYSSTAT
TMPUSER
12 rows selected
ij> select distinct ss.schemaname from sys.sysschemas ss fetch next 1 row only;
SCHEMANAME
--------------------------------------------------------------------------------------------------------------------------------
APP
I want to do a test: is 'APP' length equal to 3?
I expect query will return an empty result if test if false, otherwise run the select statement.
I hope CASE expression will help in this case, can you try the query below:
select distinct CASE WHEN length(ss.schemaname) = 3 THEN ss.schemaname ELSE '' END AS SCHEMANAME
from sys.sysschemas ss
fetch next 1 row only;

Print first 10 entries from each table in SQL Server database with empty lines between tables

I can open each table and do select top 10 * from table_name for each table and copy-paste results to Notepad. How can I automate it to have for each table print first 10 rows, followed by empty line in one output window?
This will form a statement to print first 10 rows of each table. Execute it for the desired output.
select 'Select Top 10 * From ' + SCHEMA_NAME(schema_id) + '.' + name
from sys.objects
where type = 'U'
Follow this method to get the desired output to one file: Is there any straightforward way to output text files (or CSV) from SQL Server?
Briefly: Tools-> Options-> Query results->results to text

Finding number of columns returned by a query

How can I get the number of columns returned by an SQL query using SQL Server?
For example, if I have a query like following:
SELECT *
FROM A1, A2
It should return the total number of columns in table A1 + total number of columns in table A2. But the query might be more complicated.
Here is one method:
select top 0
into _MYLOCALTEMPTABLE
from (your query here) t
select count(*)
from Information_Schema.Columns c
where table_name = '_MYLOCALTEMPTABLE'
You can do something similar by creating a view.
You didn't specify your SQL Server version but I'm assuming it's not 2012. However, future readers of this question might be on 2012+ so I'm posting this answer for them.
SQL Server 2012 provides a set of procedures to provide more meta-data about queries and parameters. In this case, the stored procedure sp_describe_first_result_set will provide a handy tabular form.
There is also a DMO function, sys.dm_exec_describe_first_result_set, to provide similar content which is what you'd want to use in your example
DECLARE
-- Your query goes here
#query nvarchar(4000) = N'SELECT * FROM mdm.tblStgBatch AS TSB';
-- Tabular results
EXECUTE sys.sp_describe_first_result_set #tsql = #query;
-- Simple column count
SELECT
COUNT(1) AS column_count
FROM
sys.dm_exec_describe_first_result_set(#query, NULL, 0);
The new metadata discovery options are replacing FMTONLY which is how one would solve this problem prior to 2012. My TSQL chops are apparently not strong enough to do anything useful with it and instead I'd have to bail out to a .NET language to work with the output of FMTONLY.
SET FMTONLY ON;
SELECT *
FROM A1, A2;
SET FMTONLY OFF;
Try this;
--Insert into a temp table (this could be any query)
SELECT *
INTO #temp
FROM [yourTable]
--Select from temp table
SELECT * FROM #temp
--List of columns
SELECT COUNT(name) NumOfColumns FROM tempdb.sys.columns WHERE object_id =
object_id('tempdb..#temp');
--drop temp table
DROP TABLE #temp
Ugly I know:
SELECT COUNT(*) +
(
SELECT COUNT(*)
FROM information_schema.columns
WHERE table_name = 'A1'
)
FROM information_schema.columns
WHERE table_name = 'A2'

SQL Server 2008 execution plan question

I have a question addressed to sql guru.
There are two tables with almost identical structure.
Based on parameter passed into the stored procedure I need to collect data from one or another table.
How to do that in a best way?
Please do not suggest to combine those tables into single one - that is not appropriate.
I did it following (MS SQL Server 2008):
Select *
FROM
String s
JOIN (
SELECT id
,TypeCode
,ProdNo
FROM Table1
WHERE #param = 1 AND TypeCode= 'INV'
UNION
SELECT id
,TypeCode
,ProdNo
FROM Table2
WHERE #param = 2 AND TypeCode= 'INV'
) m ON m.Id = s.Id
WHERE s.Id = 256
but when I looked at execution plan I was surprised because it got data from both tables in parallel threads and only after that filtered by #param value.
I thought that filtering will be made on the first stage and data collected from single table.
Is there a way to make select only from one table without splitting query into two queries and using IF operator?
Thanks
you really need to read this Dynamic Search Conditions in T-SQL by Erland Sommarskog. You shouldn't worry about repeating code, this is not some homework assignment. Just worry about making the execution plan use an index. When making SQL code "pretty" the only thing to consider is indenting & case, any other changes can cause the query plan to be slower. I've seen trivial changes to a super fast query result in a super slow query. GO FOR SPEED (index usage) and duplicate code as necessary. also see: The Curse and Blessings of Dynamic SQL
You tagged the question sql-server-2008, so if you're running SQL 2008 SP1 CU5 (10.0.2746) and SQL 2008 R2 CU1 (10.50.1702) and later, there is a new behavior (as explained in the "Dynamic Search Conditions" article linked above) of OPTION(RECOMPILE) that does not appear in all versions of SQL 2008 or in 2005. This behavior basically evaluates the #Local_Variables values at runtime and recompiles the query accordingly. In your case, this should cause one half of your UNION to be eliminated when compiling them.
Could you just use a simple IF statement?
IF #Param = 1
BEGIN
EXEC SQL
END
ELSE IF #Param = 2
BEGIN
EXEC SQL
END
ELSE
RAISERROR('Invalid Parameter', 16, 1)
Or alternatively you could build the query dynamically and execute it using the sp_executesql stored procedure.
DECLARE #Sql NVARCHAR(100)
SET #Sql = N'SELECT * FROM ('
IF #Param = 1
SET #Sql = #Sql + N'SELECT 1 a, 2 b, 3 c'
ELSE IF #param = 2
SET #Sql = #Sql + N'SELECT 4 a, 5 b, 6 c'
ELSE
RAISERROR('Invalid Parameter', 16, 1)
SET #Sql = #Sql + ') tbl'
EXEC sp_executesql #sql
First thing I'd suggest is putting the ID filter inside the union as well.
I've also changed the UNION to UNION ALL. This avoids evaluating DISTINCT rows
Select *
FROM
String s
JOIN (
SELECT id
,TypeCode
,ProdNo
FROM Table1
WHERE #param = 1 AND TypeCode= 'INV' AND id = 256
UNION ALL
SELECT id
,TypeCode
,ProdNo
FROM Table2
WHERE #param = 2 AND TypeCode= 'INV' AND id = 256
) m ON m.Id = s.Id
WHERE s.Id = 256
SQL Server's not that clever - when writing queries you should only ensure that you send the least amount of SQL to get the data you want (without sending superfluous statements), but also provide the most amount of information (via filters) where possible to give the query optimiser as many hints as possible about the data. As you've seen, it will execute all the SQL you send it.
So it sounds like you need to use dynamic-SQL from what I'm reading. This also gives you the benefit of being able to merge common parts of the SQL cutting down on the amount of duplication. For example, you could have (just taking your inner code -- you can wrap the rest of your stuff around it):
DECLARE #sql NVARCHAR(1000)
SET #sql = 'SELECT id, TypeCode, ProdCode'
IF #param = 1
SET #sql = #sql + ' FROM table1'
IF #param = 2
SET #sql = #sql + ' FROM table2'
SET #sql = #sql + ' WHERE TypeCode = ''INV'''
EXECUTE sp_ExecuteSQL #sql
Just be aware, if you're going to wind this into something more complicated, about little Bobby Tables: it's possible to abuse sp_ExecuteSQL and open gaping holes, but used correctly - with parameterised dynamic SQL - it's as good as a stored procedure.
if you can create a tDUMMY table (with a single dummy row) give this a shot.
Select
*
FROM
String s
JOIN (
SELECT
id, TypeCode, ProdNo
FROM
tDUMMY INNER JOIN Table1 ON TypeCode= 'INV'
WHERE
#param = 1
UNION ALL
SELECT
id, TypeCode, ProdNo
FROM
tDUMMY INNER JOIN Table2 ON TypeCode= 'INV'
WHERE
#param = 2
) m ON m.Id = s.Id
WHERE s.Id = 256
theoretically the query optimizer should first filter the tDUMMY table and then attempt the join. So if #param = 1 the second query should get out much faster (it will check 1 row of tDUMMY again, but it shouldn't check table2)
Note - i also made it UNION ALL (but it wouldn't have much of an impact) because one side will always return no rows anyway.