Excel parameters from SQL Query from ODBC "Error - No Value given for one or more required parameters when using question mark `WHERE XXX = ?` - sql

I have an Excel file with an SQLOLEDB connection to a MS SQL server,
When I do this with an Oracle link I can simply put WHERE XXX = ? in my query to prompt for values in Excel, however doing it with MSSQL I am getting the following error:
No Value given for one or more required parameters
When trying to parameterise a basic excel query from a value to ?
If i use values like this I get results:
SELECT *
FROM srv.stats
WHERE srv.stats.[year] = '2016'
AND srv.stats.[quarter] = '1'
When I add in the parameter ? I get the above error.
SELECT *
FROM srv.stats
WHERE srv.stats.[year] = ?
AND srv.stats.[quarter] = ?
I want to be able to do this without using VB.

Since MS SQL sources from ODBC connections don't inherently allow you to use the WHERE xxx = ? code in your SQL query (unlike Oracle connections) you need to spoof Excel to allow parameters from a MS SQL data source by building a Microsoft Query and then overwriting it.
Open a new Excel file and go to the Data tab, choose the From Other Sources drop down and select From Microsoft Query.
The Choose Data Source window will appear. Choose an existing datasource or set up a new connection to your server by selecting <New Data Source> and click OK.
Once done you will see the query wizard window open to select tables and columns, as you're going to be adding your own SQL query later just select one table that is in your query and add it to the Columns in your query: section by using the > button. For the next 2 windows just click Next and then finally Finish.
You will then be prompted to select how you want to view the data in the Import Data window, first of all click the Properties... button.
And then the Definition tab, in the Command text: box you will have a SELECT statement, below there you will need to add WHERE clauses for the amount you have in your actual query. These need to be added in the format of:
WHERE 1 = ?
AND 2 = ?
AND 3 = ?
Once this is done click OK to go back to the Import Data window and select your output type; Table, PivotTable report or PivotChart and PivotTable Report depending on what how you want to display your data.
You will then be prompted to enter a value for each parameter. If you are getting these values from Cells choose the location of them now in the order you will be putting in your actual parameters. When you have entered your parameter sources go back to the Data tab and click connections and then into the definition tab.
Now in the Command text: box paste in your actual SQL query with your parameters in the WHERE clause = ? and in the same order your defined the sources and click OK, Your data should now populate as it usually does with your parameters being used.

There is no way to prompt for inputs directly in SQL Server in the same way that you can in Access for example. You can make the values that are passed into queries like this.
DECLARE #year SMALLINT
SET #year = 2016
DECLARE #quarter TINYINT
SET #quarter = 1
SELECT *
FROM srv.stats
WHERE srv.stats.[year] = #year
AND srv.stats.[quarter] = #quarter;
You then just need to find a way that suits your solution (Excel?) to pass these. This might, for example, take the form of a stored procedure.
CREATE PROCEDURE testProcedure #year SMALLINT, #quarter TINYINT
AS
SELECT *
FROM srv.stats
WHERE srv.stats.[year] = #year
AND srv.stats.[quarter] = #quarter
GO;

Related

How to Pass Report Parameter as if it were a Query Parameter?

I am trying to make a report that allows a user to input an ID and then check this ID against several payee tables to see if the value already exists. The code requires an input and then displays a value of 1 if the code already exists and a 0 if the code is available for use. This works in the SQL code when setting the ID as a query parameter, but does not appear to work when creating an SSRS report and passing a report parameter through; the report displays no data.
I have tried adding a tablix column to display the parameter for validation. This field remains blank when attempting to pass a report parameter, but does properly display when hardcoding the query parameter.
DECLARE #id varchar(8)
SELECT #id,
CASE
WHEN #id IN (
SELECT id
FROM payee_1
)
THEN 1
WHEN #id IN (
SELECT id
FROM payee_2
)
THEN 1
WHEN #id IN (
SELECT id
FROM payee_3
)
THEN 1
ELSE 0
END as validation
The SQL query produces the results I would like to see in the SSRS report. It simply shows the input ID and a 1 if not available (or a 0 if available). When input into SSRS, the report parameter never passes through as if it were a query parameter and my report ends up being blank.
When your report is open for edit (Visual Studio), in the "Report Data" panel (docked on the left, by default), expand "Datasets". Right-click your Dataset and choose "Dataset Properties".
In the "Dataset Properties" window, on the left, click "Parameters". The parameters shown are populated from your query. If you have a parameterized query, then this will give you the option to bind your query parameters ("Parameter Value") to your report parameters, or set up other defaults or formulas, etc.

Query a database based on result of query from another database

I am using SSIS in VS 2013.
I need to get a list of IDs from 1 database, and with that list of IDs, I want to query another database, ie SELECT ... from MySecondDB WHERE ID IN ({list of IDs from MyFirstDB}).
There is 3 Methods to achieve this:
1st method - Using Lookup Transformation
First you have to add a Lookup Transformation like #TheEsisia answered but there are more requirements:
In the Lookup you Have to write the query that contains the ID list (ex: SELECT ID From MyFirstDB WHERE ...)
At least you have to select one column from the lookup table
These will not filter rows , but this will add values from the second table
To filter rows WHERE ID IN ({list of IDs from MyFirstDB}) you have to do some work in the look up error output Error case there are 2 ways:
set Error handling to Ignore Row so the added columns (from lookup) values will be null , so you have to add a Conditional split that filter rows having values equal NULL.
Assuming that you have chosen col1 as lookup column so you have to use a similar expression
ISNULL([col1]) == False
Or you can set Error handling to Redirect Row, so all rows will be sent to the error output row, which may not be used, so data will be filtered
The disadvantage of this method is that all data is loaded and filtered during execution.
Also if working on network filtering is done on local machine (2nd method on server) after all data is loaded is memory.
2nd method - Using Script Task
To avoid loading all data, you can do a workaround, You can achieve this using a Script Task: (answer writen in VB.NET)
Assuming that the connection manager name is TestAdo and "Select [ID] FROM dbo.MyTable" is the query to get the list of id's , and User::MyVariableList is the variable you want to store the list of id's
Note: This code will read the connection from the connection manager
Public Sub Main()
Dim lst As New Collections.Generic.List(Of String)
Dim myADONETConnection As SqlClient.SqlConnection
myADONETConnection = _
DirectCast(Dts.Connections("TestAdo").AcquireConnection(Dts.Transaction), _
SqlClient.SqlConnection)
If myADONETConnection.State = ConnectionState.Closed Then
myADONETConnection.Open()
End If
Dim myADONETCommand As New SqlClient.SqlCommand("Select [ID] FROM dbo.MyTable", myADONETConnection)
Dim dr As SqlClient.SqlDataReader
dr = myADONETCommand.ExecuteReader
While dr.Read
lst.Add(dr(0).ToString)
End While
Dts.Variables.Item("User::MyVariableList").Value = "SELECT ... FROM ... WHERE ID IN(" & String.Join(",", lst) & ")"
Dts.TaskResult = ScriptResults.Success
End Sub
And the User::MyVariableList should be used as source (Sql command in a variable)
3rd method - Using Execute Sql Task
Similar to the second method but this will build the IN clause using an Execute SQL Task then using the whole query as OLEDB Source,
Just add an Execute SQL Task before the DataFlow Task
Set ResultSet property to single
Select User::MyVariableList as Result Set
Use the following SQL command
DECLARE #str AS VARCHAR(4000)
SET #str = ''
SELECT #str = #str + CAST([ID] AS VARCHAR(255)) + ','
FROM dbo.MyTable
SET #str = 'SELECT * FROM MySecondDB WHERE ID IN (' + SUBSTRING(#str,1,LEN(#str) - 1) + ')'
SELECT #str
If the column has string data type you should add quotation before and after values as below:
SELECT #str = #str + '''' + CAST([ID] AS VARCHAR(255)) + ''','
FROM dbo.MyTable
Make sure that you have set the DataFlow Task Delay Validation property to True
This is a classic case for using LookUp Transformation. First, use a OLE DB Source to get data from the first database. Then, use a LookUp Transformation to filter this data-set based on the ID values from the second data-set. Here is the steps for using a LookUp Transformation:
In the General tab, select Full Cash, OLE DB Connection Manager and Redirect rows to no match output as shown in the following picture. Notice that using Full Cash provides great performance for your package.
General Setting
In the Connection tab, use OLE DB Connection Manager to connect to your second server. Then, you can either directly select the data-set with ID values or (as is shown in the picture below) you can use SQL code to select the IDs from the filtering data-set.
Connection:
Go to Columns tab and select ID columns from the both datasets. For each record from your first data-set, it will check to see if its ID is in the Available LookUp Column. If it is, it will go to the Matching output, else to No Matching output.
Match ID columns:
Click on OK to close the LookUp. Then you need to select the LookUp Match Output.
Match Output:
The "best" answer depends on data volumes and source systems involved.
Many of the other answers propose building out a list of values based on clever concatenation within SQL Server. That doesn't work so well if the referenced system is Oracle, MySQL, DB2, Informix, PostGres, etc. There may be an equivalent concept but there might not be.
For best performance, you need to filter against the second db before any of those rows ever hit the data flow. That means adding a filtering condition, as the others have suggested, to your source query. The challenge with this approach is that your query is going to be limited by some practical bounds that I don't remember. Ten, one hundred, a thousand values in your where clause is probably fine. A lakh, a million - probably not so much.
In the cases where you have large volumes of values to filter against the source table, it can make sense to create a table on that server and truncate and reload that table (execute sql task + data flow). This allows you to have all of the data local and then you can index the filter table and let the database engine do what it's really good at.
But, you say the source database is some custom solution that you can't make tables in. You can look at the above approach with temporary tables and within SSIS you just need to mark the connection as singleton/persisted (TODO: look this up). I don't much care for temporary tables with SSIS as debugging them is a nightmare I'd not wish upon my mortal enemy.
If you're still reading, we've identified why filtering in the source system might not be "doable", even if it will provide the best performance.
Now we're stuck with purely SSIS solutions. To get the best performance, do not select the table name in the drop down - unless you absolutely need every column. Also, pay attention to your data types. Pulling LOB (XML, text, image (n)varchar(max), varbinary(max)) into the dataflow is a recipe for bad performance.
The default suggestion is to use a Lookup Component to filter the data within the data flow. As long as your source system supports and OLE DB provider (or you can coerce the data into a Cache Connection Manager)
If you can't use a Lookup component for some reason, then you can explicitly sort your data in your source systems, mark your source components as such, and then use a Merge Join of type Inner Join in the data flow to only bring in matched data.
However, be aware that sorts in source systems are going to be sorted according to native rules. I ran into a situation where SQL Server was sorting based on the default ASCII sort and my DB2 instance, running on zOS, provided an EBCDIC sort. Which was great when my domain was only integers but went to hell in a handbasket when the keys became alphanumeric (AAA, A2B, and AZZ will sort differently based on this).
Finally, excluding the final paragraph, the above assumes you have integers. If you're performing string matching, you get an extra level of ugliness because different components may or may not perform a case sensitive match (sorting with case sensitive systems can also be a factor).
I would first create a String variable e.g. SQL_Select, at the Scope of the Package. Then I would assign that a value using an Execute SQL Task against the 1st database. The ResultSet property on the General page should be set to Single row. Add an entry to the Result Set tab to assign it to your Variable.
The SQL Statement used needs to be designed to return the required SELECT statement for your 2nd database, in a single row of text. An example is shown below:
SELECT
'SELECT * from MySecondDB WHERE ID IN ( '
+ STUFF ( (
SELECT TOP 5
' , ''' + [name] + ''''
FROM dbo.spt_values
FOR XML PATH(''), TYPE).value('(./text())[1]', 'VARCHAR(4000)'
) , 1 , 3, '' )
+ ' ) '
AS SQL_Select
Remove the TOP 5 and replace [name] and dbo.spt_values with your column and table names.
Then you can use the variable SQL_Select in a downstream task e.g. an OLE DB Source against database 2. OLE DB Sources and OLE DB Command Tasks both let you specify a Variable as the SQL Statement source.
You could add a LinkedServer between the two servers. The SQL command would be something like this:
EXEC sp_addlinkedserver #server='SRV' --or any name you want
EXEC sp_addlinkedsrvlogin 'SRV', 'false', null, 'username', 'password'
SELECT * FROM SRV.CatalogNameInSecondDB.dbo.SecondDBTableName s
INNER JOIN FirstDBTableName f on s.ID = f.ID
WHERE f.ID IN (list of values)
EXEC sp_dropserver 'SRV', 'droplogins'

Create an insert script from select results

Using SQL Server Management Studio is there a way I can select one or more rows in the grid of select results and have SQL Server Mangement Studio generate one or more insert statements (one for each row selected) which would insert that data into a table with the same schema?
Edit: I know how to create one manually, but I was hoping there would be something that would create it automatically for me. If you are familiar with Toad there is a way to have Toad generate inserts based on data in the results pane and I was hoping SSMS had an equivalant function.
Try to save the query result into a disposable table.
For example:
SELECT * INTO disposable_customer_table FROM customer_table WHERE id IN (in range of something)
Then do a db -> Tasks -> Generate Scripts.
Select specific database objects.
Choose disposable_customer_table from the list of table names.
Choose Save to file.
Make sure to do an Advance setup and select "Data only" from the 'Types of data to script'.
Tweak the result file and rename the disposable_customer_table back to the original table name.
Clean it up and drop the disposable_customer_table.
select 'insert into tableB values (', tableA.x ,',',tableA.y,',',tableA.z,')' from tableA
I think you have two options here:
Create your inserts manually. For instance:
select Name, Surname,
'insert into Person (Name,surname) values ('''+Name+''','''+Surname+')'
from Person
This gets you the results and, in the last column, the insert script for the row. You can then select and paste it in an Editor window.
Right click on the db -> Tasks -> Generate Scripts. Press then Advance and select "Data Only" (Default is Schema Only).
Perform your query and right click on the blank area where the column headers meet the row number in the Results view.
You can then select Script Grid Results:

db2 sql query in excel

I have a number of very simple queries that I run for others on my team and am placing them in Excel so that the end user can just execute the query themselves by opening the spreadsheet.
I'm currently using an ODBC driver to connect to the DB2 server.
All queries work fine but one is giving me a headache in getting it to work correct in Excel.
One of the queries has a where statement that equals a different value depending on the situtation.
ex.
SELECT *
FROM TABLE1 T1
WHERE T1.T1_ID = 859745
What I would like is to set it up so that the query runs like the following. Is it possible to do this through a variable somehow?
SELECT *
FROM TABLE1 T1
WHERE T1.T1_ID = "USER ENTERED VALUE FROM COLUMN A ROW 1 IN THE EXCEL SHEET"
I'm assuming you're using Microsoft Query. First add a parameter to the query. Then you can customize your parameter to choose an excel cell an the input for the paramter

SQL Server 2008 column select

In SQL Server, if I got a table with like 20 columns and I want 18 of them, can I say something like * minus columnname1, columnname2, course right now I write them all.
But if you could it would be much easier.
Little hint to replace the asterisk with column names in SQL Management Studio in no time without any fancy plugin:
Select your written query (no matter how many joins, etc.)
Right click and select "Design Query in Editor..."
Simply click "Ok"
The asterisk should have been expanded to column names now :)
Ofc it's possible to select/deselect any column in the query editor..
Hth
It is not possible. However if you are using SQL Server Management Studio 2008 / 2005 you can right click on the table and select the "Script Table as > SELECT To" menu option. This will save you typing the column names, or purchase Red-Gate's SQL Prompt
Out of the box - no, it's not possible. You have to spell out all the columns you want explicitly.
With SQL Server Management Studio 2008, there is intellisense which can help you select columns from a table - so that's certainly one step to help ease the pain.
Add-in tools like SQL Prompt offer more help - in SQL Prompt, you can type
SELECT *
FROM dbo.YourTable
and if you have the cursor just after the asterisk symbol (*), you can press <TAB> and expand the asterisk into the list of all columns for that table (and then remove the two you don't want) - or you can popup a window and pick those columns you really want.
Very handy, very useful, very much speeding up development - but it's not a free tool.....
You can use select TOP (18) * from givenTable is you want 18 rows.
There is no such method for columns. In fact column names are stored in master db and you can extract them and consruct query looking like what you are asking for BUT it would not be easier than just select field1,field2 ... field18 from blaBlaBla.
SELECT table_name=sysobjects.name,
column_name=syscolumns.name,
datatype=systypes.name,
length=syscolumns.length
FROM sysobjects
JOIN syscolumns ON sysobjects.id = syscolumns.id
JOIN systypes ON syscolumns.xtype=systypes.xtype
WHERE sysobjects.xtype='U'
and sysobjects.name='myTableName'
ORDER BY sysobjects.name,syscolumns.colid
will give you the list of your columns. You can write select generator based on this query.
I'd like to add to the answer of, "No, it's not possible directly in SQL". I would love to have that feature too! It sucks when you're trying to do some quick debugging on a 10+ column table that has a varbinary(max).
But I really just want to point out an alternative to Kane's tip for SSMS 2008 (Sql Server Management Studio).
If you open the Object Explorer (right-click in the query window and choose "Open Server in Object Explorer"), navigate to the node for the table in question. Expand the node so you can see the "Columns" node. Now "drag" the Columns node over to your query window and "drop" it. It will paste in all the column names for the table--and you can use it directly in a SELECT clause.
It is not possible as far as I know.
I have created a script for easy copy/pasting multiple columns, you might find it useful. See:
http://www.sqlservercentral.com/scripts/102375/
The script is explained in detail there, but in short for those who do not have an account on sqlservercentral:
It's a stored procedure that i can run using a shortcut. Type in your tablename (also works with temp tables and views), highlight it, hit the shortcut and it will display the columns of the table. From there you can easily copy multiple columns (the columns are also shown with a comma in front of the column name, so that also saves you some typing) and paste it in your query screen.
CREATE PROCEDURE [dbo].[sp_ColumnSelect]
#FullObjectName varchar(200)
AS
/*
Author: Robin van Schaik
Version: 1.3 (03-OCT-2012)
*/
DECLARE #Object varchar(200)
DECLARE #Schema varchar(200)
DECLARE #Database varchar(200)
DECLARE #IsTempTable bit
-- Break down parameter in Database/Schema/Object
SET #Object = PARSENAME(#FullObjectName,1)
SET #Schema = ISNULL(PARSENAME(#FullObjectName,2),'dbo')
SET #IsTempTable = case when left(#Object,1)='#' then 1 else 0 end
SET #Database = case when #IsTempTable=1 then 'tempdb' else PARSENAME(#FullObjectName,3) end
EXEC(
'SELECT
b.Name as ColumnStart
, '',''+b.Name as ColumnNext
, ''[''+b.Name+'']'' as ColumnStartBr
, '',[''+b.Name+'']'' as ColumnNextBr
FROM
' +#Database+'.sys.objects a
INNER JOIN
' +#Database+'.sys.columns b
ON a.object_id=b.object_id
INNER JOIN
' +#Database+'.sys.schemas d
ON a.schema_id=d.schema_id
WHERE
a.Object_ID=OBJECT_ID('''+#Database+'.'+#Schema+'.'+#Object+''')
AND d.name = '''+#Schema+'''
'
)