Get unique entries only using Where Statement? - sql

I got the following set up: Oracle Database as back-end, a interface provided by our IT-Database Department and my Excel-VBA front-end.
The interface provides a variety of functions to insert, update and read data. The "DB_connection.read_test" function takes inputs:
TestID (0=ALL)
SQL Where Statement
Table name where the data will be stored
Set the header of the database as boolean true= set, false= leave as is
The function shows a view that the back-end guy prepared for me. Now there are two columns that I want to compare if they are unequal "PRODID" and "ArticleID" and get all unique pairs of "PRODID" and "ArticleID". The challenge is here that I only can write a where statement.
Sub query_ProdID_uneq_ArticleIDlocal()
Dim SQL_Where As String
SQL_Where = "PRODID <> ARTICLEID"
Call DB_connection.read_test(0, SQL_Where, "Pruefungen_Tab", True)
End Sub
This code above works but gets me all data where PRODID <> ARTICLEID. So they are not unique. I could work on in VBA-Excel and reduce the pairs to distinct pairs.
But is there a way to make this happen with the right WHERE Statement?
SQL_Where = "PRODID <> ARTICLEID and Unique(ArticleID)"
Or something like this?
Example Data:

Related

Joining two tables listing all the results from the first table

I have two tables related to each other. The first table has one or more number of the same attribute on the latter table. (there is a one-to-many relationship) thus the rows on the latter table includes basically the following columns: (PK, FK, attribute). Some rows of the first table does not have an attribute and are not recorded in the latter table.
I would like to list all the rows in the first table with an extra column for the attribute from the latter table. when I use inner join or where clause I cannot list the results that have no attribute. However what I want to do is to list all the rows from the first table, and if there is no attribute for it I want to make the extra column null, and if it has more than one attribute I want to concatenate them in that single column.
I am aware that I am not sharing any code here and I am asking the question in an abstract way because first I need to know which tool to use and in what way. I tried joins and sub-queries with select statement as well as methods such as ISNULL but I was not sure what I was doing and it did not work for me.
PS: I am new to the site as you can observe. Thus if you think this is not a proper way of asking questions and if you suggest removing this question off the forum I will no question asked.
SELECT TableA.*, TableB.Attribute
FROM TableA LEFT JOIN TableB ON TableA.PK = TableB.FK
Edit: I just read the original question more fully and realised that this is not quite what you're after. This will give you the NULL value you wanted if there is no related data in TableB, however you will need to write a function if you want multiple related values in TableB concatenated into the one row for a given TableA record. Create a new module and in here add the following function. I am assuming here that your PK and FK columns are Long Integer and the Attribute values are String. I am also assuming that you're working in MS-Access.
Public Function ListAttributes(lngPK As Long) As String
Dim rsAttributes As DAO.Recordset
Dim strResult as String
Set rsAttributes = CurrentDB.OpenRecordset("SELECT Attribute FROM TableB WHERE FK = " & lngPK)
Do While Not rsAttributes.EOF
strResult = strResult & ", " & rsAttributes!Attribute
rsAttributes.MoveNext
Loop
If strResult <> "" Then
strResult = Mid(strResult, 3)
End If
ListAttributes = strResult
Set rsAttributes = Nothing
End Function
Your query then becomes
SELECT *, ListAttributes(PK) FROM TableA
Note that you will now get an empty string instead of NULL for rows in TableA that have no corresponding rows in TableB, but you can always use an IIf function call to fix this if necessary.

Can't figure out how to pass SQL results (single field value) to update another field in a different table

I think I have a very simple question that I can’t answer. I have two tables: tblAVAILABLE and tblWORKING.
tblAVAILABLE has three fields:
fFILE
fSHIFT
fSENIORITY
tblWORKING has two fields:
fSPOT
fFILLEDBY
The fields tblAVAILABLE.fFILE and tblWORKING.fSPOT are both unique
I have SQL that will query the tblAVAILABLE table to find the most senior person available for the shift. I can get that to work.
I need to pass the field tblAVAILABLE.fFILE to a string so I can update the tblWORKING.fFILLEDBYfield.
Any suggestions?
You can use a sub query to do this.
UPDATE tblWORKING
SET fFILLEDBY = (/* YOUR FIND MOST SENIOR PERSON QUERY */)
WHERE fSPOT = /* WHATEVER */;
Just make sure your sub query is only returning 1 value and only 1 value.

SQL Query in Access to prompt with Message Box asking to change table name

Is there a way to be prompted before you a run an SQL query in Access, to enter in the table name that you wish to query? For example, lets say the columns will always stay constant. The columns could be called "Fruit" and "Date." But, the table name could change depending on the batch number. Ie. table name could be "BatchNO_1" or "BatchNO_2" or "BatchNO_3" etc. So Lets say i have an SQL like:
select Fruit, Date from BatchNO_1 where Fruit = "Apples"
Is there a way that I can be prompted to enter in the table name and have the SQL use the table name i enter to perform the query?
No. The table name cannot be passed as parameter to a query. You will have to construct the query yourself.
Dim tableName as String, sql As String
tableName = InputBox("Please enter the table name")
If tableName <> "" Then
sql = "SELECT Fruit, Date FROM [" & tableName & "] WHERE Fruit = 'Apples'"
'TODO: execute the query here
End If
For instance, you could change the query text of an existing query like this:
CurrentDb.QueryDefs("myQuery").SQL = sql
Or you could execute the query like this
Dim db As DAO.Database, rs As DAO.Recordset
Set db = CurrentDb
Set rs = db.OpenRecordset(sql)
Do Until rs.EOF
Debug.Print rs!Fruit & " " & rs!Date
rs.MoveNext
Loop
rs.Close: Set rs = Nothing
db.Close: set db = Nothing
By putting the batch number in the table name instead of as a column, you are encoding data in the schema. This is not best practice, so in my opinion, the correct answer is to change your database design.
Make a single Batch table with all the columns from your current BatchNo tables, but add a column named BatchNo as part of the primary key. Load all the data from your BatchNo tables into this one, and then delete those tables. Then your query will straightforwardly look like this:
SELECT Fruit, Date
FROM Batch
WHERE
Fruit = "Apples"
AND BatchNo = [Enter Batch No];
Don't put data in table names. That is not the way databases are supposed to be made.
Just to explain a little bit, the reason that your current design violates best practice is due to exactly the problem you are facing now--the shenanigans and weird things you have to do to work with such a design and try to perform operations in a reasonable, data-driven, way.
By having the user enter the table name, you also create the danger of SQL injection if you aren't also careful to compare the user-provided table name to a whitelist of allowed table names. While this may not be such a big deal in Access, it is still heading down the wrong path and is training for something else besides professional database work. If you would ever like to grow your career, it would be regrettable to first have to unlearn a bunch of stuff before you could even start with a "clean slate" to learn the right way to do things.

Jet engine (Access) : Passing a list of values to a stored procedure

I am currently writing a VBA-based Excel add-in that's heavily based on a Jet database backend (I use the Office 2003 suite -- the problem would be the same with a more recent version of Office anyway).
During the initialization of my app, I create stored procedures that are defined in a text file. Those procedures are called by my app when needed.
Let me take a simple example to describe my issue: suppose that my app allows end-users to select the identifiers of orders for which they'd like details. Here's the table definition:
Table tblOrders: OrderID LONG, OrderDate DATE, (other fields)
The end-user may select one or more OrderIDs, displayed in a form - s/he just has to tick the checkbox of the relevant OrderIDs for which s/he'd like details (OrderDate, etc).
Because I don't know in advance how many OrderID s/he will select, I could dynamically create the SQL query in the VBA code by cascading WHERE clauses based on the choices made on the form:
SELECT * FROM tblOrders WHERE OrderID = 1 OR OrderID = 2 OR OrderID = 3
or, much simpler, by using the IN keyword:
SELECT * FROM tblOrders WHERE OrderID IN (1,2,3)
Now if I turn this simple query into a stored procedure so that I can dynamically pass list of OrderIDs I want to be displayed, how should I do? I already tried things like:
CREATE PROCEDURE spTest (#OrderList varchar) AS
SELECT * FROM tblOrders WHERE OrderID IN (#OrderList)
But this does not work (I was expecting that), because #OrderList is interpreted as a string (e.g. "1,2,3") and not as a list of long values. (I adapted from code found here: Passing a list/array to SQL Server stored procedure)
I'd like to avoid dealing with this issue via pure VBA code (i.e. dynamically assigning list of values to a query that is hardcoded in my application) as much as possible. I'd understand if ever this is not possible.
Any clue?
You can create the query-statement string dynamically. In SQL Server you can have a function whose return value is a TABLE, and invoke that function inline as if it were a table. Or in JET you could also create a kludge -- a temporary table (or persistent table that serves the function of a temporary table) that contains the values in your in-list, one per row, and join on that table. The query would thus be a two-step process: 1) populate temp table with INLIST values, then 2) execute the query joining on the temp table.
MYTEMPTABLE
autoincrementing id
QueryID [some value to identify the current query, perhaps a GUID]
myvalue one of the values in your in-list, string
select * from foo
inner join MYTEMPTABLE on foo.column = MYTEMPTABLE.myvalue and MYTEMPTABLE.QueryId = ?
[cannot recall if JET allows ANDs in INNER JOIN as SQL Server does --
if not, adjust syntax accordingly]
instead of
select * from foo where foo.column IN (... )
In this way you could have the same table handle multiple queries concurrently, because each query would have a unique identifier. You could delete the in-list rows after you're finished with them:
DELETE FROM MYTEMPTABLE where QueryID = ?
P.S. There would be several ways of handling data type issues for the join. You could cast the string value in MYTEMPTABLE as required, or you could have multiple columns in MYTEMPTABLE of varying datatypes, inserting into and joining on the correct column:
MYTEMPTABLE
id
queryid
mytextvalue
myintvalue
mymoneyvalue
etc

Using a query to loop through tables that are similar in structure but have different names

I would like to use a query to loop through tables that are similar in structure but have different names (ie. tableJan2011, tableFeb2011, tableMar2011 etc.)
Is there a way in MS Access and in SQL Server to use the same query statement while varying the table name within it. (similar to using parameter values) (need this to add different input to each different month's table)
This is a bad table design. You should have a singe table, where you have a column(s) to indicate month/year. You would then just query this single table and add a WHERE month='X' and YEAR='Y' to limit your results to what you need.
without a table redesign use UNION and clever WHERE clause parameters, which will cause rows to only come from the table that applies.
SELECT
..
FROM tableJan2011
where...
UNION
SELECT
..
FROM tableFeb2011
where...
UNION
SELECT
..
FROM tableMar2011
where...
First off, listen to the people who are telling you to use one table. They know of which they speak.
If you can't do that for some obscure reason (such as inheriting the design & not being allowed to change it), then you're stuck writing VBA code. There's no way that I know of, in Access, to substitute source tables (or even source columns--values only), in a saved QueryDef.
You'll need something like this:
Private Function QueryTable (strTableName as String) As DAO.Recordset
Const theQuery as String = "SELECT tbl.* FROM [table] As tbl"
Dim sSql As String
Dim db As DAO.Database
Dim rs As DAO.Recordset
sSql = Replace(theQuery, "[table]", strTableName)
Set db = CurrentDb()
Set rs = db.OpenRecordset(sSql)
Set QueryTable = rs
End Function
Note that this is simplified code. There's no error handling, I haven't released the objects (which I usually do, even though they'll go out of scope), and SELECT * is almost always a bad idea.
You'd then call this function wherever you need it, passing in the name of the table.
consider moving the year and month out of the table name and into columns in one table.
you can create a table with query or table names to use at runtime, but you have to be able to write Access BASIC code in a module.
Here's an example, assuming you have a query built on a table with the query names you want to execute:
Set db = CurrentDb
Set rsPTAppend = db.OpenRecordset("qry_PTAppend")
rsPTAppend.MoveFirst
Do Until rsPTAppend.EOF
qryPT = rsPTAppend("PT")
Set qdef = db.QueryDefs(qryPT)
sqlOld = qdef.sql
sqlNew = sqlOld
' manipulate sql
If sqlNew <> sqlOld Then
qdef.sql = sqlNew
End If
db.QueryDefs(rsPTAppend("append")).Execute
If sqlNew <> sqlOld Then
qdef.sql = sqlOld
End If
rsPTAppend.MoveNext
Loop
Don't know what is possible in Access but in SQL Server you could create a view that use union to get all tables together and then build your queries against the view.
One other option you have could be to build your queries dynamically.
In sql server you can execute a string as sql.
http://msdn.microsoft.com/en-us/library/ms175170.aspx
I'm not aware of anything similar in MS Access (though my experience is limited). You could however dynamically generate your sql in code to accomplish this. Perhaps you could create a function to take the table suffix and parameters and build the desired sql that way.