Adding parameterized predefined query to datagridview - database

For the life of me, I haven't been able to find this anywhere (other than solutions that exclude using the tableAdapter....
I have a simple setup. I have a datagridview. I have a tableadapter that is bound to the datagridview. This all works marvelously, but brings in the entire table. On the tableadapter itself, I have created a query called "nonServers" which returns the data I want.
So my question is, how can i bind the results of the tableadapter's QUERY to the datagridview, rather than the full results? When I try to add the query, it adds a button up top to trigger the query (as it's meant to do). But this is not what I want - i want the initial value to be the results of the query only.
any help appreciated, i reckon it's probably embarassingly simple

You're under some misconceptions. Firstly, your grid is not bound to a table adapter. The adapter is just a means to move data back and forth between the database and the application. The data is stored in the application a DataTable that is part of a DataSet. That's what your grid is bound to.
The default query for each table adapter is a SELECT * with no WHERE clause, so all columns and all rows. The table adapter has two methods - Fill and GetData - that execute that query. Fill will populate an existing DataTable, which will probably be part of a DataSet, while GetData creates and returns a new, standalone DataTable.
When you add a new query with a WHERE clause to filter, you are prompted to name the new methods that will execute that query. They are named FillBy and GetDataBy by default and you're supposed to append a meaningful suffix to that, e.g. if you were to filter by a ParentId column then you'd name the methods FillByParentId and GetDataByParentId. I'm not sure what exactly your query looks like but you might go with FillWithNonServers and GetDataWithNonSevers.
If you drag a table from the Data Sources window onto your form then that will add a bunch of controls and components by default and it will also generate some code, including a call to the Fill method of the appropriate table adapter to the Load event handler. In your case, you simply have to change that Fill call to a call to your new method and it will then invoke your custom query instead of the default.

Related

Why am I unable to load a subform when it is bound to a specific stored procedure?

Summary
In a Microsoft Access 2010 database (accdb), I have a form that dynamically loads other forms into a subform object on the main form. The forms used in the subform object are bound to ODBC pass-through queries that execute stored procedures to return recordsets. I can't figure out why I can use one sproc and it works perfectly fine, but if I bind the form to another sproc, it fails to load the subform.
Technical Walkthrough
I have two pass-through ODBC queries. qryGood and qryBad. They use identical ODBC connection strings (ODBC;DRIVER=SQL Server;SERVER=MyServer;UID=MyUser;Trusted_Connection=Yes;DATABASE=MyDatabase), and the SQL behind them is identical, but pointing to two different SQL stored procedures on the SQL 2012 database server.
qryGood source: exec spGoodProc 123456
qryBad source: exec spBadProc 123456
The SQL behind the sproc is very simple. Return records from a single table, filtering by the ID passed as a parameter. (Some will do more complex things, but I am just focusing on a simplified example here that demonstrates the problem.)
The RecordSource property of frmMySubform is set to qryBad.
The subform SourceObject is set via VBA code: sfrmMain.SourceObject = "frmMySubform" No errors are thrown at this point. While the SourceObject property now returns frmMySubform, the .Form object does not seem to be set.
I then try to reference a property on the subform: Debug.Print sfrmMain.Form.Name This fails with error 2467: The expression you entered refers to an object that is closed or doesn't exist.
I can then open frmMySubform in design view, change the RecordSource property to qryGood and it works just fine. This seems to point to a problem with spBadProc that only manifests itself when used as the RecordSource on a subform.
What I Have Tried
In an effort to troubleshoot this problem, I have used the process of elimination to narrow this down as far as I can, but I am still not understanding why the one sproc works and the other doesn't. Both return records just fine in SQL and when running the pass-through query directly. Both work fine when opening the form directly. It only becomes a problem when the form is set as a SourceObject in a subform control.
I have used sp_procedure_params_rowset to compare the parameters in the sprocs, and they are identical. I have compared the data types of the columns in SQL and there is nothing new or different in tblBad that isn't in tblGood. I have also tried profiling the SQL server while setting the form, and it seems to call the sproc just fine. I didn't see any clues when comparing the trace between the bad and the good calls.
Setting the RecordSet directly to an ODBC link to tblBad works just fine (and I presume a view would be fine as well) but having the simple stored procedure wrapper somehow triggers the error.
I have also compared the security, properties and extended properties for spGoodProc and spBadProc and they are identical.
My Question
What can I do on the troubleshooting side to reduce this down further? Has anyone out there encountered similar issues with bound sprocs on subforms? I am working on a very complex database with hundreds of forms, tables and queries, so I would really like to understand why this is occurring before I go too far down this path.
Thanks in advance for any insight you are able to share on this perplexing problem. :-)
Found it!
After tracing it back to something with the specific table, I removed all constraints, keys, and then columns from a copy of the table, systematically testing to see if I could pinpoint the problem. Sure enough, it was a specific column name in the stored procedure!
Simply aliasing this column to a different name solved the problem. (See below for expanded details)
Update after Further Testing
After additional testing to further pinpoint the issue, I think I now understand why this was occurring. When you link an ODBC table and specify a unique (key) column, Access will automatically attempt to set the LinkMasterFields and LinkChildFields to the key column name when a subform is loaded and the subform has a column with the same name. While this works fine with linked tables or views, it does not work when the RecordSource of the subform is set to a stored procedure.
If you attempt to do this by manually adding the subform, you will see the following notification:
However, if you set the subform target through VBA code, you don't get any warning or error message. It simply doesn't (fully) load the subform. #Albert D. Kallal, you were right on about this being related to a master/child fields issue!
I was able to consistently reproduce the issue in a test database file in both Access 2010 and Access 2016. If you would like to see this for yourself, you can use the following steps to reproduce it:
Create a SQL table with a PrimaryID column.
CREATE TABLE [dbo].[tblBugTest](
[PrimaryID] [int] NOT NULL,
[TestColumn] [nchar](10) NULL
) ON [PRIMARY]
Add a couple test records to the table you just created.
Create a Stored Procedure to return the records.
CREATE PROCEDURE [dbo].[spBugExample]
AS SELECT * FROM tblBugTest
Create a blank Microsoft Access database (accdb).
Using ODBC, create a linked table to tblBugTest.
Important: Select PrimaryID as the unique column.
Create a pass-through query named qryPassThrough using ODBC to the same database, and set the SQL to exec spBugExample.
Open the query to verify that it returns records.
Create three blank forms in the database. frmMain, frmSubForm, and frmBlank.
Add frmBlank as a subform to frmMain. Name the subform sfrmSubform.
Set the RecordSource of frmMain to the linked table.
Add a button to frmMain to switch the subform from frmBlank to frmSubForm.
Private Sub cmdShowBug_Click()
With Me.sfrmSubform
.SourceObject = "frmSubForm"
Debug.Print .LinkMasterFields
Debug.Print .LinkChildFields
Debug.Print .Form.Name
.SourceObject = "frmBlank"
End With
End Sub
Set the RecordSource of frmSubForm to qryPassThrough.
Drop a couple bound controls onto frmSubForm.
Test frmSubForm by itself. It should load a record from tblBugTest.
Open frmMain and click the button. It should throw an error.
If you step through the code, you will see that before setting the SourceObject, the LinkMasterFields property is blank. After setting SourceObject, you can hover over LinkMasterFields and see that it is now set to the PrimaryID column.
Workarounds
Changing any of the following will work around the error by avoiding the problematic auto-linking of the master/child fields.
Delete and relink the linked table, this time not specifying a unique column.
Alias the column in the Stored Procedure to a different name than the unique column.
Clear the RecordSource property of the parent form.
Clear the subform RecordSource property and set the RecordSet after loading the subform.
Use a view or linked table instead of a stored procedure in the subform.
Keep in mind that the subform data source will be attempted to be loaded BEFORE the main form loads. What this suggests is that on the main form's load event, you will
First setup the PT query.
Then set the OBJECT source of the sub form.
In other words, the source object of the sub form control should be blank.
You code then to set up the PT query will be:
With currentdb.queryDefs("qryGood")
.SQL = "EXEC spGoodProc " & 123456
end with
Of course you can replace the 123456 with a varible, or even a value from a text box (from the main form).
Now that the PT query is setup, you THEN are to set the form that the sub-form is to load.
So, after above code, we then have:
me.mySubForm.SourceObject = "name of subform goes here"
So, it should be about a total of 4 lines of code. And as above shows, you don't even need any connection string stuff in your VBA code.
So, just keep in mind:
Setup the PT query as per above. You can then launch a report, or even a form, or in this case set the form that the sub-form control is to load. This also suggests/hints that you need to remove the source object of the sub form control (leave it blank).
You can have the sub-from source object set, but then this would suggest that you setup the PT query source as per above BEFORE you launch the main form with the sub form based on the PT query. As noted, this set of steps is required since the sub form actually load and resolves it data source BEFORE the main form displays and renders. So, by leaving the source-object blank for the sub form, then you the developer re-gains complete control over the order of loading.

Pulling a sub-set of an SQL Server Dataset using comboboxes

I am trying to "convert" a more complex Access database/user interface into a custom app using VB .NET. I am also a NOOB in .NET so please be gentle.
Here are the steps I'm trying to take to duplicate my results in .NET
1) Create a Data Source (dsServer) to my SQL Server that pulls all tables and all data. I created this Data Source in my project already using the UI.
2) Create a Data Table (dtResults) in my Data Source that pulls all the info I need from 3 tables:
[Person] - This is a single entry list.
[Primary Teacher] - This is linked through column (Primary) on the [Person] table. This will return single results.
[Test] - This is liked by [Person].ID. This table has multiple entries per [Person]
I have also created this data table in my project under the data source using the UI.
3) Populate 12 comboboxes on a form that will be used to "filter" my results onto a DataGridView. Each combobox needs to be a DISTINCT list of what is in dtResults. Each time a combobox changes, the results in the DataGridView need to be updated as well as all the other comboboxes. (narrowing the choices to only what is available)
4) Create a DataGridView on the same form that will display the filtered results.
I don't seem to have any issues with creating the datasource or the datatable. What I can't figure out is how to populate the comboboxes from the datatable or how to bring back my filtered results to the datagridview.
Any help on the best way to accomplish this would be great.
It sounds like you want a single results gridview to be filtered by the optional selection of 8 different combo boxes.
When you generate the query for the gridview, you'll need to selectively include the where clauses for each combo box with a value. That should be as simple as sql += " AND Combobox2Column = #Combobox2Value " for each combo box. Don't bother using the column like '%' pattern for combo boxes with no selection. That can have negative performance impacts and is not necessary if you build your query in VB before sending to the database.
(This is a scenario when using "WHERE 1=1" as the first predicate in the where clause comes in handy. It does have a little bit of overhead, but it standardizes all the rest of the predicates to always start with AND to prevent the additional overhead of determining if the specific combo box is the only combo box with a value selected. If your query requires any other filters not based on the combo box, this becomes unnecessary)
Another important factor is that changing any combo box will trigger the event to refresh the contents of the grid view. Put your combo box refresh logic in a dedicated method, separate from an event handler. Then you can easily call that method from the event handler for each combo box (or any other control/event).
Hope that helps.

SSRS Error - "Report item expressions can only refer to fields within the current dataset scope or, if inside an aggregate

I'm new to SSRS and I'm not sure if it will do what I would like to do.
I'm getting the following error in SSRS:
"The Value expression for the text box 'Textbox17' refers to the field 'DayCnt'. Report item expressions can only refer to fields withing the current dataset scope or, if inside an aggregate, the specified dataset scope. Letters in the names of fields must use the correct case."
I have a list that's 3 rows and 3 columns. I need to use multiple data sets across the rows and columns. I would like everything on the report to be grouped on the school ID, which each dataset does have.
I will be using multiple datasets per cell in some cases and I'm using a textbox and then dragging the dataset field into it. I'm not sure if grouping is the problem. I'm not sure how to group the entire list at once, or if it is row based, or how the grouping works with a list with multiple columns.
How can I get everything in the list to be based off of the school ID?
You can reference more than one dataset in the same data region (table, etc.) but only if it makes sense to use aggregate functions on all the datasets except the primary one that your grouping is based on. I'm not sure if this makes sense for your use case or not.
An aggregate function is something like First. If you don't specify the dataset, it defaults to the "current dataset." The current dataset is an invisible default that you can't see or set anywhere in the UI, as far as I can tell.
=First(Fields!MyField.Value)
vs.
=First(Fields!MyField.Value, "MyDataset")
Now, there are a couple of tricky things to know about the Report Builder UI.
When you drag a field into your report, the expression it creates does not specify the dataset.
When you drag a field into your report, it changes the current dataset!
This leads to exasperating behavior such as the following:
Create a data region.
Set its grouping to a field from Dataset1.
Drag in a field from Dataset1.
Run the report. It works!
Drag in a field from Dataset2. RB will automatically use an aggregate function, as you would expect.
Run the report again. Now you get an error, not on either of your fields, but on the grouping of your data region.
The problem is that dragging in the field from Dataset2 changed the current dataset to Dataset2 and broke everything that uses Dataset1 without explicitly specifying it. The solution is hacky:
Manually change the expressions of all your fields from Dataset2 so they explicitly reference the dataset, or
Reset the current dataset by dragging in a field from Dataset1 and deleting it.
You cannot use different datasets on only one SSRS table. One table should only refer to one dataset.
And the solution to your question is: recreate your dataset (query), try to get one dataset by using distributed query if they are on different server instance, or specify database name when they are on the same server.
You might have done some modification on the Dataset and you did not refreshed the fields.

Filtering a subform table based on parameters in a form

I have a form with two unbounded comboboxes that work fine (one lists several age values and the other lists several region). I have a subform, which is a query that refers to the comboboxes, but I can't get it to update when I change values in the comboboxes.
My query is:
Select * from mastertable where (age=[form]![masterform]![age] and region=[form]![masterform]![region]
The query works in the sense that if I run it and input the parameters manually it works. It also works in the sense that if I create a button that runs the query on the form, it produces the correct table/query.
My question is how do I get the query to work as a subform? I'd like to be able to select values in the combo box and see the subform update, rather than having to click on a button that runs the query separately from the form.
I also tried creating a table that the parameters on the form can bound to, then relating those variables to the mastertable, but that also didn't work.
How do I get a table/query subform to update as information about the parameters changes in the form? I'm guessing it will require some VBA code on the "after update" event associated with the comboboxes. Any idea how to do this?
Thanks
I figured it out. I just put a little VBA code that requeries the subtable after update.
Code is [subform].requery

How to display results from Stored Procedure WinForms

I have a stored procedure in SQL Server that returns different structures based on the parameters.
In other words, it might return a result set with three fields, or 15, and the column names are going to be different.
How can I display these results in a WinForm app?
I currently use the Entity Framework for accessing data, but obviously that is not going to work in this situation.
The data will be readonly, ie, no need to edit. Just need to display it.
I am guessing that I need to skip EF and just call the SP directly, and populate a DataGridView with autocolumns.
Greg
Since you're retrieving a dynamic data structure you'll need to make your grid columns dynamic too. What I would do is, after retrieving the result set successfully, to clear the Columns collection and recreate them from scratch every time you called the SP and the data needs to be refreshed on-screen. Do not use AutoGenerateColumns, as they provide little chance to customize the look of them, instead define each and every column yourself so that you can choose what to display and how.
This is the best answer I found: How to use a DataAdapter with stored procedure and parameter
In short, use a DataAdapter and a DataTable. Even with AutoGenerateColumns, the column headers look great and it works no matter the table structure that comes back.
Greg

Resources