Sorry the title is not very descriptive but it is a tricky problem to word.
I have some data, about 200 or more rows of it, and each row has a PacketID, so several rows belong in the same packet. What I need to do, is convert all the PacketIDs from (Example - BDFD-2) to just a number (Example - 1) so all the entries with a packet identifier x need to have a packet identifier of say 3. Is there an SQL query that can do this? Or do I just have to go through manually.
You asked about a query. I wrote a quick VBA procedure instead just because it was so easy. But I'm unsure whether it is appropriate for your situation.
I created tblPackets with a numeric column for new_PacketID. I hoped that will make it clearer to see what's going on. If you truly need to replace PacketID with the new number, you can alter the procedure to store CStr(lngPacketID) to that text field. So this is the sample data I started with:
PacketID new_PacketID packet_data
BDFD-2 a
R2D2-22 aa
BDFD-2 b
R2D2-22 bb
EMC2-0 aaa
EMC2-0 bbb
And this is the table after running the procedure.
PacketID new_PacketID packet_data
BDFD-2 1 a
R2D2-22 3 aa
BDFD-2 1 b
R2D2-22 3 bb
EMC2-0 2 aaa
EMC2-0 2 bbb
And the code ...
Public Sub RenumberPacketIDs()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim lngPacketID As Long
Dim strLastPacketID As String
Dim strSql As String
strSql = "SELECT PacketID, new_PacketID" & vbCrLf & _
"FROM tblPackets" & vbCrLf & _
"ORDER BY PacketID;"
Set db = CurrentDb
Set rs = db.OpenRecordset(strSql)
With rs
Do While Not .EOF
If !PacketID <> strLastPacketID Then
lngPacketID = lngPacketID + 1
strLastPacketID = !PacketID
End If
.Edit
!new_PacketID = lngPacketID
.Update
.MoveNext
Loop
.Close
End With
Set rs = Nothing
Set db = Nothing
End Sub
I think an approach like that could be fine for a one-time conversion. However if this is an operation you need to perform repeatedly, it could be more complicated ... especially if you need each PacketID replaced with the same number from one run to the next ... eg. BDFD-2 was replaced by 1 the first time, so must be replaced by 1 every time you run the procedure.
If you only have a few packet IDs, you can just use update:
UPDATE table_name
SET PacketID =
(
CASE PacketID
WHEN 'BDFD-2' THEN 3
WHEN 'ABCD-1' THEN 5
ELSE 2
END
)
The ELSE is optional.
I am not sure why you even want to convert the packet ids to a number, they seem perfectly fine as they are. You could create a table of packets as follows
SELECT DISTINCT TableOfRows.Packet_id AS PacketId INTO Packets FROM TableOfRows;
You can then use this to select the packet you are interested in and display the corresponding rows
Related
I’ve got a simple table in Microsoft Access that looks like this:
Primary Key
Applications List
123
<Value>|<Value>,<Value>|<Value>
456
<Value>|<Value>,<Value>|<Value>
I need to break out the list of applications into separate rows using the “,” as a delimiter so the end result is a table that looks like this:
Primary Key
Applications List
123
<Value>|<Value>
123
<Value>|<Value>
456
<Value>|<Value>
456
<Value>|<Value>
I’ve tried using the Split function but can’t figure out how to split on the “,” and output the results to a different row like the second table above. I would greatly appreciate your help figuring this one out. Thanks so much!
If you can put a limit on the length of Application List you can build a number table
CREATE TABLE NumTable (ID integer)
up-front with as many rows as there are characters in the longest Application List. Assuming that the longest [Application List] is 1000 characters long, ID=1, ID=2, ..., ID=1000), and then use something like this:
select T.[Primary Key],
mid(T.AL,NT.ID+1
, iif(instr(NT.ID+1,t.AL,',')>0
, instr(NT.ID+1,t.AL,',')-1-nt.ID,1000)) as
from (select [Primary Key], ',' & [Application List] as AL from tblYourTable) as T
inner join
NumTable as NT
on mid(t.AL,NT.ID,1)=','
You can build the number table in EXCEL and paste it; or write a little VBA routine, or even create it dynamically (*).
I can't imagine it performing very well if the volume is high.
Let us know how you proceed!
(*)Generate numbers 1 to 1000000 in MS Access using SQL
If you setup a Table1 and a Table2 with the same field names, you can run this function to do it for you - it basically loops through Table1 records, splits the Applications List into multiple fields and then inserts new records for each field.
Public Sub SplitDataIntoNewTable()
Const DATA_SEPARATOR As String = ","
Dim qdf As DAO.QueryDef
Dim rsOld As DAO.Recordset
Dim rsNew As DAO.Recordset
Dim i As Integer
Dim lngNumAdded As Long
Dim lngKey As Long
Dim strList As String
Dim strNewList As String
Dim varLists As Variant
Set rsOld = CurrentDb.OpenRecordset("Table1", dbOpenDynaset, dbReadOnly)
Set rsNew = CurrentDb.OpenRecordset("Table2", dbOpenDynaset)
With rsOld
While Not .EOF
lngKey = ![Primary Key]
strList = ![Applications List]
varLists = Split(strList, DATA_SEPARATOR)
With rsNew
For i = LBound(varLists) To UBound(varLists)
' Add new record for every list split out of old data
.AddNew
' Note that this CANNOT actually be defined as a PRIMARY KEY - it will have duplicates
![Primary Key] = lngKey
![Applications List] = varLists(i)
lngNumAdded = lngNumAdded + 1
.Update
Next i
End With
.MoveNext
Wend
rsNew.Close
.Close
End With
MsgBox "Added " & lngNumAdded & " New Records"
Set rsOld = Nothing
Set rsNew = Nothing
End Sub
For example I had Table1 look like this:
And resulting Table2 ended up like this
This is the code in HPE UFT that successfully runs the query and displays a msg box.
I would like it to store the query results or at least 1 column of the query results in the HPE UFT data table so that I can run a loop on those loan numbers later.
Set objConnection = CreateObject("ADODB.Connection")
Set objRecordSet = CreateObject("ADODB.Recordset")
objConnection.open "provider=123ABC;Server=T1;User Id=****;
Password=****; Database=i_prod;Trusted_Connection=Yes"
sqlQuery="SELECT Table1 AS LoanNumber, lm.loanrecordid, clm.istexasconversion as TexasConversion FROM Table1 lm WITH (NOLOCK) LEFT JOIN Table2 clm WITH (NOLOCK) ON clm.lenderdatabaseid = lm.lenderdatabaseid AND clm.loanrecordid = lm.loanrecordid Where clm.istexasconversion IS NOT NULL"
objRecordSet.open sqlQuery, objConnection
value = objRecordSet.fields.item(0)
msgbox Value
objRecordSet.Close
objConnection.Close
Set objConnection = Nothing
Set objRecordSet = Nothing
This is the query used in SQL.
SELECT
lm.loanid AS LoanNumber
,Column1
,column2 as Texas
FROM table1 lm WITH (NOLOCK)
LEFT JOIN table2 clm WITH (NOLOCK)
ON clm.lenderdatabaseid = lm.lenderdatabaseid
AND clm.loanrecordid = lm.loanrecordid
Desired Result Image
To start with, it helps if you define the parameter names initially in the table like this:
Datatable.AddParameter("LoanNumber", dtGlobal)
Datatable.AddParameter("LoanID", dtGlobal)
Datatable.AddParameter("TexasConversion", dtGlobal)
This will set the first three columns of the Global datatable with the name of the parameter you are going to insert.
Then, for ease of use, put the data in your RecordSet into an Array:
myArray = objRecordSet.GetRows ' do this before you close the recordset
And finally, loop around the two dimensional array to populate the table with data:
For myLoop = 0 to UBound(myArray, 2) ' loop over the total rows
DataTable.SetCurrentRow(myLoop + 1) ' +1 as row count starts from 1 not 0
Datatable("LoanNumber") = myArray(0, myLoop)
Datatable("LoanID") = myArray(1, myLoop)
Datatable("TexasConversion") = myArray(2, myLoop)
Next
And if you need to store a copy of the datatable (unless you plan to only use this data during the run, you will need to):
Datatable.Export("\\Path\To\File\To\Save.xlsx")
If you have any trouble following this, post a comment and I'll try to explain further.
You can use database output values to output the values from your column.
Steps to create database output values:
Select Insert> Output Values> Database Output Values
Create connection string
Choose in Query definition 'Specify SQL statement manually' radio button and maximum number of rows
In Sql statement edit field insert your query
There are three mdb files in folders on a network drive that may hold the required record(s). How do I determine which db holds the record(s), ideally without data transfer/linking/etc.? Then a single SQL or DAO select can get the data from the correct db. Note: I'm trying to use Access as a front end to SQL using existing Access data spread all around the network drives.
My current solution of configuring 3 DAO objects and checking for no results, in succession until found, seems to load the remote tables to the local recordset and takes too long.
Is there a way to use IF EXISTS in this scenario?
This code throws "Invalid SQL statement; expected DELETE,INSERT,PROCEDURE,SELECT,OR UPDATE" error but is generally what I'd like to do :
Dim strSQL As String
Dim strSku As String
Dim intDbToSearch As Integer
strSku = DLookup("SKUNo", "tblCurrentItem") 'Note: this returns valid SKU#
strSQL = "IF EXISTS(SELECT xxTable.SKUNo "
strSQL = strSQL & "FROM [S:\Our Inventory\Cust Sleeves.mdb].[xxTable] "
strSQL = strSQL & "Where xxTable.SKUNo = " & "'" & strSku & "') Then intDbToSearch = 1"
DoCmd.RunSQL strSQL
This is one of three IF Exists that would run if SKUNo not found in db 1 or 2.
Ultimately intDbToSearch should point to db 1,2,or 3 if SKUNo found or 0 if not.
Thanks
In the end, I pushed usage rules for the 3 databases upstream and can now predetermine which database to search. Thanks again for your input.
Will the sought for SKU always occur in only 1 of the tables?
If you don't want to set table links or use VBA recordsets, only other approach I can see is a query object with a dynamic parameter that references a form control for the SKU input. No idea if this will be faster and will need a query for each remote table.
SELECT SKUNo FROM xxTable IN "S:\Our Inventory\Cust Sleeves.mdb" WHERE SKUNo = Forms!formname!cbxSKU
Then just do DCount on the query.
Dim intDbToSearch As Integer
If DCount("*", "xxQuery") > 0 Then
intDbToSearch = 1
End If
Could UNION the SELECT statements so would have only 1 query object to work with.
SELECT "x1" AS Source, SKUNo FROM xxTable IN "S:\Our Inventory 1\Cust Sleeves.mdb" WHERE SKUNo = Forms!formname!cbxSKU
UNION SELECT "x2", SKUNo FROM xxTable IN "S:\Our Inventory 2\Cust Sleeves.mdb" WHERE SKUNo = Forms!formname!cbxSKU
UNION SELECT "x3", SKUNo FROM xxTable IN "S:\Our Inventory 3\Cust Sleeves.mdb" WHERE SKUNo = Forms!formname!cbxSKU;
How about a simple Function to check if exists by passing the table name and value?
Something like this:
Public Function ExistInTable(Byval TableName As String, ByVal Value As String) As Boolean
ExistInTable = (DCount("*", TableName, "[SKUNo]='" & Value & "'" > 0)
End Function
To call it:
Sub Test()
If ExistInTable("T1", "Whatever") Then 'Exists in T1
If ExistInTable("T2", "Whatever") Then 'Exists in T2
'....
End Sub
I have a query over a few tables and get a result in the form of:
SomeId Input
1 2
1 5
2 3
2 1
1 2
I'd like to be able to sum by Id as a third field, so I would get
SomeId Input subTotal
1 2 2
1 5 7
2 3 3
2 1 4
1 2 9
Is it possible?
Thanks
Here's a couple other ideas. Both have their drawbacks though. Both of them involve using a regular query.
First idea: Calling a VBA function to keep track of totals.
The drawback is that you have to order your table by SomeID.
Also the running total only resets itself when the function gets a different SomeID even if it's a different query. This means that the value of SomeID on the first record must be different than the last record of the last previous query.
SELECT SomeTable.SomeId, SomeTable.SomeInput, MyRunningTotal([SomeID],[SomeInput]) AS SubTotal
FROM SomeTable
ORDER BY SomeTable.SomeId;
Function MyRunningTotal(SomeID As Long, SomeInput As Long) As Long
Static LastSomeID As Long
Static RunningTotal As Long
If SomeID <> LastSomeID Then
RunningTotal = 0
LastSomeID = SomeID
End If
RunningTotal = RunningTotal + SomeInput
MyRunningTotal = RunningTotal
End Function
Second Idea: Using DSum. This is basically a query within a query.
The drawback is that for large recordsets it can be very slow. This is because it has to run a separate query for every record.
Also, you have to add an Auto-increment field (in the sample code below it's ID).
SELECT SomeTable.ID, SomeTable.SomeId, SomeTable.SomeInput,
DSum("SomeInput","SomeTable","[SomeID]=" & [SomeID] & " and [ID]<=" & [ID]) AS SubTotal
FROM SomeTable;
Yes, it's certainly possible, but with the problem as stated it cannot be accomplished by using just Access SQL queries. The two issues are:
The source data has no sequential key value (so a self-join with a <= condition cannot be used)
The source data is not sorted by SomeId (suggesting that the order may have some other significance), which would further complicate a set-based approach.
Fortunately, the VBA required to do this is not too involved:
Sub CreateSubtotals()
Dim cdb As DAO.Database, rst As DAO.Recordset
Dim dct As Object '' Dictionary
Set dct = CreateObject("Scripting.Dictionary") '' New Dictionary
Set cdb = CurrentDb
'' 'dct' will hold the running totals for each 'SomeId'
Set rst = cdb.OpenRecordset( _
"SELECT DISTINCT SomeId " & _
"FROM qryYourOriginalQuery", _
dbOpenSnapshot)
Do While Not rst.EOF
dct.Add rst!SomeId.Value, 0
rst.MoveNext
Loop
rst.Close
'' create new table to hold results
cdb.Execute _
"SELECT SomeId, Input, 0 AS subTotal " & _
"INTO tblYourDataWithSubtotals " & _
"FROM qryYourOriginalQuery", _
dbFailOnError
'' fill in the 'subTotal' column
Set rst = cdb.OpenRecordset("tblYourDataWithSubtotals", dbOpenTable)
Do While Not rst.EOF
dct(rst!SomeId.Value) = dct(rst!SomeId.Value) + rst!Input.Value
rst.Edit
rst!subTotal.Value = dct(rst!SomeId.Value)
rst.Update
rst.MoveNext
Loop
rst.Close
Set rst = Nothing
Set dct = Nothing
Set cdb = Nothing
End Sub
We have a fairly large Oracle database that we are able to connect via Microsoft Access and ODBC with read-only access. We work with a front-end system that does not match the structure behind the scenes and often I need to query the system via Microsoft Access. The problem is that we are not provided any documentation as to structure and the structure needs serious attention. Searching for the field that I need is very time consuming.
With our front end, I'm able to view the values that I want to query, and I know the key fields, but I need to find the field that contains the known value.
If I have a record where I know the value of field "A", and have the value of field "X", is it possible to query field "X"?
Front end shows
Student ID: 12345678
Payments: 23456
Back end
TechID: 12345678
???: 23456
Can I query "???"
You can do this by iterating over the collection of tables, and for each table, the collection of fields.
Open Database
Get all Tables
For Each Table
Get all Fields
For Each Field
If Field type is text ... and
If Field size is not TOO Long ...
Search for string
If found, write to a results bucket
Next
Next
Here is an example of code for cataloging tables (source here)
Public Function GenerateDataDictionary(aDataDictionaryTable As String)
'*** Usage: GenerateDataDictionary("MyDataDictionaryTable")
'*** Extracts the information about the tables for the data dictionary
'*** and inserts it to a table named aDataDictionaryTable
Dim tdf As TableDef, fldCur As Field, colTdf As TableDefs
Dim rstDatadict As Recordset
Dim i As Integer, j As Integer, k As Integer
Set rstDatadict = CurrentDb.OpenRecordset(aDataDictionaryTable)
Set colTdf = CurrentDb.TableDefs
'Go through the database and get a tablename
For Each tdf In CurrentDb.TableDefs
'Do what you want with the table names here.
rstDatadict.AddNew
rstDatadict.Update
rstDatadict.AddNew
rstDatadict![Table] = tdf.NAME
rstDatadict![Field] = "----------------------------"
rstDatadict![Display] = "----------------------------"
rstDatadict![Type] = ""
rstDatadict.Update
rstDatadict.AddNew
rstDatadict![Table] = "Table Description:"
For j = 0 To tdf.Properties.Count - 1
If tdf.Properties(j).NAME = "Description" Then
rstDatadict![Field] = tdf.Properties(j).Value
End If
Next j
rstDatadict.Update
rstDatadict.AddNew
rstDatadict.Update
For i = 0 To tdf.Fields.Count - 1
Set fldCur = tdf.Fields(i)
rstDatadict.AddNew
rstDatadict![Table] = tdf.NAME
rstDatadict![Field] = fldCur.NAME
rstDatadict![Size] = fldCur.Size
Select Case fldCur.Type
Case 1
FieldDataType = "Yes/No"
Case 4
FieldDataType = "Number"
Case 8
FieldDataType = "Date"
Case 10
FieldDataType = "String"
Case 11
FieldDataType = "OLE Object"
Case 12
FieldDataType = "Memo"
Case Else ' Other values.
FieldDataType = fldCur.Type
End Select
rstDatadict![Type] = FieldDataType
For j = 0 To tdf.Fields(i).Properties.Count - 1
If fldCur.Properties(j).NAME = "Description" Then
rstDatadict![DESCRIPTION] = fldCur.Properties(j).Value
End If
If fldCur.Properties(j).NAME = "Caption" Then
rstDatadict![Display] = fldCur.Properties(j).Value
End If
If fldCur.Properties(j).NAME = "Rowsource" Then
rstDatadict![LookupSQL] = fldCur.Properties(j).Value
End If
Next j
rstDatadict.Update
Next i
Debug.Print " " & tdf.NAME
Next tdf
End Function
You can catalog your findings in Access by making a table of field-names which joins to a table of table-names. Then your searches are based on the catalog instead of raw collections.
I reverse-engineered the schema for MAS 90 (with JobOps add-in) this way. There's no map, but I had a read-only ODBC connection which I used in precisely the way you propose. The purchasing accountant would give me a distinctive Product Number and I'd run it through this comprehensive engine. Over time I succeeded in distilling 700 tables comprising 18k fields down to 20 tables and a few hundred fields. That allowed us to export our data.
The answer to your question is simple. No, you cannot do that.
There are two solutions that I can think of. The first is to manually concatenate all the values together and then look for the row that contains the value. This is imperfect, but might work:
select *
from (select t.*, ('|'""col1||'|'||col2+'|' . . .||'|') as allcols
from t
) t
where instr('|23456|', allcols) > 0
This would find any row that has that value in a column. Probably close enough for what you want.
The second is to use UNPIVOT to do essentially the same thing.
I would strongly suggest that you invest a little bit of time to find the mapping between the fields, and then create a view in Oracle that has the field names as seen in the application. It sounds like this would save you a lot of effort in the medium term.