Parameterize Source in Power Query when connecting to PostgreSQL DB - excel

Using the .Net data provider for postgresql, I would like to create an excel workbook that loads some tables from a given schema. The schema is set using a named range in the excel workbook, the table names are the same for each schema.
I tried the following:
Define a query "from other sources" / "blank query" named SchemaIdParam as
let
rng= Excel.CurrentWorkbook(){[Name="schemaid"]}[Content]
in
rng
(the name "schemaid" is defined in the workbook.)
Define a query "from PostgreSQL db" named mytable as
let
src = PostgreSQL.Database("xxx.myhost.com:5235", "my_database"),
tbl = src{[Schema=SchemaIdParam,Item="mytable"]}[Data]
in
tbl
Now this does not work. The error message states: "[Expression.Error]: no match between key and rows in table" (own translation). Yet it works if I replace SchemaIdParam by a literal value in quotation marks. Then the correct table is delivered.
Any hints how I can resolve this are very appreciated!
The reason why I want to use a named range for the schema name is that I want to programmatically, outside from excel, set the schema name. I am very open to suggestions how to do this in another way.

It could be a type error. Try converting the SchemaIdParam value to text as part of that query or else try
tbl = src{[Schema=Number.ToText(SchemaIdParam),Item="mytable"]}[Data]

After a lot of trying, I found the answer. I had a problem defining SchemaIdParam. A working definition is:
let
rng= Excel.CurrentWorkbook(){[Name="jobid"]}[Content],
value = rng{0}[Column1]
in
value
i.e., I had to reference a specific cell in the named range.

Related

Overwrite Existing Data in Access Using transferspreadsheet

I have a macro that saves monthly data to an Access database in Access 2013 using the DoCmd.TransferSpreadsheet method. A possible situation I want to account for is if data is saved to the database and then someone realizes that the data was wrong and they want to rearchive the data for the month in question after they fix the data. In other words, is there a way for me to change my code to overwrite data for the same month as the data that will be archived?
One of the fields in the data that is archived is the date, and it also has a field name as well. Another important thing to know is that the data that needs to be overwritten would all be in the same month, but not necessarily the same day. Here's a simplified example of what the "my_data" array would look like in excel:
Sub excel_export()
Dim xls_path As String
xls_path = "C:\mywbk.xlsm"
Dim db_path As String
Dim db_obj As Access.Application
db_path = "C:/mydb.accdb"
Set db_obj = New Access.Application
Call DoCmd.TransferSpreadsheet(acImport, acSpreadsheetTypeExcel12Xml, "TableName", _
xls_path, True, "my_data")
db_obj.CloseCurrentDatabase
Set db_obj = Nothing
End Sub
Don't import the spreadsheet, link it as a table.
Then, first, create a simple select query that filters the linked table and converts, say, string dates to real dates.
Now, use this query as source for a combined update and append query for your Access table as described here:
Update and Append Records with One Query

Query referencing another query

I can't execute a query in Power Query and the error that throws me is like:
Formula.Firewall: Query XXX references other queries or steps, so it may not directly access a data source. Please rebuild this data combination.
The code within this query is as below:
let
CallToFunction = myFunction,
#"Invoked Function" = CallToFunction(),
Source = Oracle.Database("myServer", [Query="SELECT * FROM myTable WHERE CustomerPK IN (" & #"Invoked Function" & ")"])
in
Source
myFunction is a function that uses a couple of other queries and eventually returns a string of primary keys that I can use to fill in the parenthesis of the WHERE clause of my SQL statement.
When I invoke the function alone it works correctly, so this must be an issue of how to call the function within the last query.
Any ideas?
You need to set the privacy settings of your data sources & workbook.
See https://support.office.com/en-ca/article/Privacy-levels-Power-Query-cc3ede4d-359e-4b28-bc72-9bee7900b540?ui=en-US&rs=en-CA&ad=CA

Copy ADO Recordset using loop

I tried and failed to edit records from an ADODB recordset that I populate with an SQL (Original Question. So then I decided to go the old fashioned (and inefficient) way and copy the recordset onto a fresh new one record by record.
I start by setting the field properties equal (Data Type and Size), since I want to make sure I get a correct data match. However, I encounter two errors:
"Non-nullable column cannot be updated to Null"
and
"Multiple-step operation generated errors. Check each status value"
(Which was exactly what I was trying to avoid by looping!)
Here is the code:
'Create recordset
Set locRSp = New ADODB.Recordset
'Copy fields (same data type, same size and all updateable (which is the final goal)
For Each Field In locRS.Fields
locRSp.Fields.Append Field.Name, Field.Type, Field.DefinedSize, adFldUpdatable
Next
'Copy records
locRSp.Open
locRS.MoveFirst
'Loop original recordset
Do While Not locRS.EOF
locRSp.AddNew
'Loop all fields
For Each Field In locRS.Fields
locRSp.Fields(Field.Name) = locRS.Fields(Field.Name)
Next
locRS.MoveNext
Loop
What I dont understand is:
If I am copying the original field properties (Size and Type), why would it give data errors!?
Is there some other property I need to be looking at? How?
For the first problem: Simply, if you want to store Null values, you need to set the attribute to "adFldIsNullable"
So for my example I changed the append call to:
locRSp.Fields.Append Field.Name, Field.Type, Field.DefinedSize, adFldIsNullable
For the second problem: When the query is downloaded to the original recordset the field properties are set I guess depending on the data itself. But in this case, I went one by one investigating what that was and found that the problem column was set to:
Data Type adNumeric
Which needs to have a precision and scale defined. Where precision is how many digits you want, and scale is number of decimals
So in my case I added an IF to the loop that copies the fields:
If Field.Type = 131 Then '131 is the constant value for adNumeric
'Define Scale
locRSp.Fields(Field.Name).NumericScale = 0
'Define Precision
locRSp.Fields(Field.Name).Precision = 4
End If

ADO on Access Lookup field, getting wrong field returned

I'm in Excel 2010 VBA, using ADO 2.8 to query an Access 2010 database. I don't own the database and don't have any authority to make any changes to it. I've been working with Excel VBA for many years but my Access knowledge is sketchy.
Using the same SQL as one of the queries in the database (copied from the Access query and pasted into Excel VBA), I get different results in some fields than that query in the database gets.
For the affected fields, I see that in Access those fields are defined as lookups. Here's an example lookup from one of the affected fields' Row Source property:
SELECT [Signers].[SignerID], [Signers].[SignerName] FROM Signers ORDER BY [SignerID], [SignerName];
In the Access database, where the SQL statement refers to that field, the query returns SignerName.
But in my ADO code, where the very same SQL statement refers to that field, the query returns SignerID, not SignerName.
Is there something I can do from my ADO code to get SignerName instead of SignerID, from the same SQL statement? Or do I need to modify the SQL statement?
Thanks,
Greg
Update:
On the Access side, I think I see now why only SignerName appears. On the field's Lookup tab, the Column Widths property is:
0";1.2605"
So I guess SignerID is there in the Access query result but with a column width of 0.
Unfortunately that doesn't help me on the ADO side. Any suggestions on getting SignerName instead of SignerID in the ADO query result?
Update2:
Here's a sample SQL statement that returns different fields depending on either it's in Access or in ADO:
SELECT MasterAccount.[SignerKey1]
FROM MasterAccount ;
Per Preet's request, here's the ADO code in Excel VBA:
strDatabasePath = rgDatabasePathCell.Value 'rgDatabasePathCell is a worksheet cell object.
strPWD = DATABASE_PASSWORD
Set cnn = New ADODB.Connection
cnn.Provider = "Microsoft.ACE.OLEDB.12.0"
cnn.ConnectionString = "Data Source='" & strDatabasePath & "';Jet OLEDB:Database Password='" & strPWD & "';"
cnn.Open
Set cmd = New ADODB.Command
cmd.ActiveConnection = cnn
cmd.CommandType = adCmdText
cmd.CommandText = strSQL
Set rst = New ADODB.Recordset
rst.Open cmd.Execute
shMA.Cells(2, 1).CopyFromRecordset rst 'shMA is a worksheet object.
Update 3:
It occurred to me that from what I've said so far, it might seem like I could just change this:
SELECT MasterAccount.[SignerKey1]
FROM MasterAccount ;
to this:
SELECT [Signers].[SignerName]
FROM MasterAccount ;
But there are 13 affected lookup fields, all with exactly the same "Row Source" property text as shown above, and all return different SignerName items for each row. I don't know why they are all returning different items per row; I have not been able to find any difference in the way they are defined. I've been tasked with getting the same result in Excel as that Access query gets.
Update 4:
VBlades -- Thanks, I found the form that has a tab with dropdowns for each of the 13 SignerKey-n fields. If I right-click that form and choose Form Properties, the RecordSource property is:
SELECT MasterAccount.*, Bank.BankRating FROM Bank INNER JOIN MasterAccount ON Bank.BankID = MasterAccount.Bank;
However I don't understand how that would be selecting a different SignerName item for each of the 13 SignerKey-n fields, or what to do with this information to get the same results in ADO as in the Access query. Any suggestions?
Update 5:
I may be close to a workaround. If I do this, I get the SignerName field for SignerKey1:
SELECT Signers.SignerName
FROM Signers RIGHT JOIN MasterAccount ON Signers.SignerID = MasterAccount.SignerKey1.Value;
And if I do this, I get different SignerName items for each field on each row:
SELECT Signers.SignerName, Signers_1.SignerName, Signers_2.SignerName
FROM Signers AS Signers_2 INNER JOIN (Signers AS Signers_1 INNER JOIN (Signers RIGHT JOIN MasterAccount ON Signers.SignerID = MasterAccount.SignerKey1.Value) ON Signers_1.SignerID = MasterAccount.SignerKey2.Value) ON Signers_2.SignerID = MasterAccount.SignerKey3;
That works both in an Access query and in ADO. Next step, I'll try to add these joins to the main SQL statement.
Update 6:
Well, when I try to add even one of those 13 joins to the main SQL statement, it works fine in an Access query, but in ADO I get the error:
Row handles must all be released before new ones can be obtained.
So I'm stuck. Any suggestions?
I've raised the issue with the database owner, but they don't know why the affected fields' Row Source property includes SignerID, so I'm not sure if that's going to help.
You can do the following (simplest way)
Exclude [Signers].[SignerID] from the Query
SELECT [Signers].[SignerName] FROM Signers ORDER BY [SignerID], [SignerName];
Or, create Composite field containing both [SignerID], [SignerName] and extract any part using VBA that you are familiar with:
SELECT ([SignerID] & "_" & [SignerName]) As Composite FROM Signers ORDER BY [SignerID], [SignerName]
Regards,
Okay, I worked around the problem by getting a separate recordset of the SignerID and SignerName fields from the Signers table.
Then I looped through all the rows of each affected field, looked up SignerID in the 2nd table, and swapped in SignerName for SignerID in the original table.
I tried to do that in ADO, but got the error "An UPDATE or DELETE query cannot contain a multi-valued field". So instead I made the swaps after copying the recordset to the Excel worksheet.
I would have liked to know how to handle it all in ADO, but this works. All is well now.

Get members of a group in Lotus Domino

I retrieve names from a group using formula, and put them in a field of type Names this way:
#Name([CN];NAME)
I want to manipulate this data in my code, but using Lotusscript. Can't find it at Google or Lotus Domino's Help. Is there a way I can handle this?
In LotusScript there is a class named "NotesName" to do such manipulations.
If there is a field named "NAME" in you document, then the code would look like:
Dim doc as NotesDocument
Dim nnName as NotesName
'Somehow get the document, using ws.CurrentDocument.document
'or db.UnprocessedDocments.GetFirstDocument, depends on your situation
Set nnName = New NotesName( doc.GetItemValue("NAME")(0) )
Whatyourlookingfor = nnName.Common
If NAME is a Multivalue then you would have to write a loop to get the common- name for every element in the array doc.GetItemValue("NAME")
The next time you have a question, check out the language cross reference in the help...
There it tells you, what the LotusScript- Pendant for #Name is.
Please try with below suggestion for getting list of person names from group.
First need to check the availability of searching group on names.nsf (All the groups are available on "($VIMGroups)" view.
if the group is available means you need to get the list of values from "Members" item
The members item have variant(list) values. So need to iterate the members for getting each value
Please refer the below sample code:
Set namesDb=session.GetDatabase(db.Server,"names.nsf")
Set groupVw=namesDb.GetView("($VIMGroups)")
Set groupDoc=groupvw.GetDocumentByKey("groupname")
persons= groupDoc.members
Forall person In persons
Msgbox person
End Forall
You can use the Evaluate method. It will return you the result of a Notes Formula:
Dim result as Variant
formula$ = "#Name([CN];NAME)"
result = Evaluate(formula$)
If the formula needs to be evaluated within the context of a document, you can pass that document as a second parameter to the method.
More info here

Resources