I'm using MS-ACCESS database.
From the prject I use and made some other questions the table NOEUDS and INFRA (that should be updated):
Table INFRA:
RECNO - NOEUD - SECURISE
00000008 C002 F
00000005 C009 F
00000001 C035 F
00000002 C001 F
00000003 C036 F
00000006 C012 F
00000007 C013 F
TABLE NOEUDS:
NOEUD TYPE_MAT N_AMONT
C021 COF 100
C022 COF 229
C023 COF 130
C002 COF 111
I want to create a query that checks on NOEUDS the nodes C* that are missing inside INFRA table, if not should be inserted a new one.
The problem is the RECNO field that works as a control and can not be duplicated (not primary key because all the DB is only a repositoty for the program that controls it).
All the fields are text so RECNO is a consecutive counting using HEX numbers as shown.
I used the query to select:
SELECT (SELECT MAX(CINT(INFRA.RECNO))+1 AS N FROM INFRA),
NOEUDS.NOEUD, "F" AS Expr2
FROM NOEUDS
WHERE (((NOEUDS.NOEUD) Like "C*"
And (NOEUDS.NOEUD) Not In (SELECT NOEUD FROM INFRA)));
The result was:
9 C021 F
9 C022 F
9 C023 F
SHOULD BE:
9 C021 F
A C022 F
B C023 F
I need some help on this one so I can insert the correct RECNO in hexadecimal counting after 00000019 passes to 0000001A and so on.
thanks in advance
UPDATE 1:
The program we use uses a Access database as storage. When I add a noeud using the program I have to insert some more info using the menus needed for the maps and as built information. The problem is that a lot info is redundant and the program can not handle it automatically. I am trying to work lees and insert the possible information using querys.
Every time I insert a noeud in noeuds table, is needed to insert a line in INFRA table only with RECNO (sequential counting from the last one), the NOEUD and some other info (to complete the autocad table tag). Since I have hundreds of Cxxx, Bxxx, Pxxx, Gxxx equipments I sabe for each project some hour of boring work.
I need help on counting a sequential way of adding RECNO for each NOEUD found in NOEUDS table that will be inserted in INFRA table.
UPDATE 2:
I'm inserting each noeud by hand. Is it possible to join in a way that it takes the list from the noeuds that I want to insert and insead of doing 1 by 1 it takes the list and does in a sequence?
the 2 queries are these:
Equipes I want to add at table INFRA:
SELECT NOEUDS.NOEUD
FROM NOEUDS
WHERE (((NOEUDS.NOEUD) Like "C*" And (NOEUDS.NOEUD) Not In (SELECT NOEUD FROM INFRA)));
Insertion by hand:
INSERT INTO INFRA ( recno, NOEUD, SECURISE )
SELECT (SELECT Right(String(8, "0") & Hex(Max(Val("&H" & RECNO)) + 1), 8) AS N FROM INFRA), NOEUDS.NOEUD, "F" AS Expr2
FROM NOEUDS
WHERE (NOEUDS.NOEUD=[INSERT CHAMBRE?]);
I think a VBA solution should be better than trying to do what you want with only SQL. If you don't have much VBA experience, it could still be achievable because the required VBA should be fairly basic. See if this code outline is enough to get you started.
Public Sub AddToInfra()
Const cstrQuery As String = "qryUnmatchedNoeuds" ' Note 1 '
Dim db As DAO.Database ' Note 2 '
Dim fld As DAO.Field
Dim rsFrom As DAO.Recordset
Dim rsTo As DAO.Recordset
Set db = CurrentDb
Set rsFrom = db.OpenRecordset(cstrQuery, dbOpenSnapshot)
Set rsTo = db.OpenRecordset("infra", dbOpenTable, dbAppendOnly)
Do While Not rsFrom.EOF
rsTo.AddNew
For Each fld In rsFrom.Fields ' Note 3 '
If Not fld.Name = "RECNO" Then
rsTo.Fields(fld.Name).Value = fld.Value
End If
Next fld
rsTo!RECNO = Next_InfraRecno ' Note 4 '
rsTo!SECURISE = "F" ' Note 5 '
rsTo.Update
rsFrom.MoveNext
Loop
rsTo.Close
rsFrom.Close
Set fld = Nothing
Set rsFrom = Nothing
Set rsTo = Nothing
Set db = Nothing
End Sub
Notes:
I used a saved query based on my best guess as to what you want. See the SQL below.
DAO.Database requires a reference to Microsoft DAO Object Library. If your Access version is 2000 or maybe Access XP, you may need to set that reference (from VBE main menu, Tools->References).
I decided the destination table would include fields which match the name and data type of the fields in the source recordset. If that doesn't work for you, substitute something like this for each of the common fields: rsTo!YourFieldNameHere.Value = rsTo!YourFieldNameHere.Value (And drop .Value if you prefer.)
Create a Next_InfraRecno() function to return the next RECNO value. Translate the approach we used earlier into a function. Post a new question if you run into trouble ... show us your code, error message and line which triggers the error (if any), and anything else we need to know. :-)
I got the impression you want SECURISE = "F" for each of the inserted rows.
In a comment you mentioned "Use field ANCIEN for storage of counting". I don't know what's involved for that and hope, whatever it is, you can integrate it into this code outline. If not, sorry. :-(
Here is the SQL for my qryUnmatchedNoeuds query:
SELECT n.DELETED, n.NOEUD
FROM
noeuds AS n
LEFT JOIN infra AS i
ON n.NOEUD = i.NOEUD
WHERE
(((n.NOEUD) Like "c*")
AND ((i.NOEUD) Is Null));
Although I don't understand your question very well, I hope this answer provides something you can use.
My INFRA table has a text column named RECNO. The table contains one row.
RECNO
00000019
This query give me "1A" as N.
SELECT Hex(Max(Val("&H" & RECNO)) + 1) AS N
FROM INFRA;
To pad N with zeros to a width of 8, I can use this query which gives me "0000001A" as N.
SELECT Right(String(8, "0") & Hex(Max(Val("&H" & RECNO)) + 1), 8) AS N
FROM INFRA;
Regarding the rest of your question, my instinct would be to open a recordset containing the unmatched NOEUDS.NOEUD values, then move through the recordset rows and insert each NOEUD value, your custom RECNO sequence number, and the "other info" into the INFRA table.
Related
I have a table from where I want to display records on the page grouped by categories in Classic ASP in such order that the Latest records are displayed on top. I am using MS SQL 2005 server.
For example table is like
ID Data1 Data2 Category
1 abc def test1
2 rrr 344 test2
3 ttt edf test1
4 www ghj test2
sql="select * from TABLE where Category='test1" and Category='test2' order by ID desc"
I want to display the all the data from category Test1 and Test2 on the page at different places. How to separate results by category?
Thanks,
If you want to display the results in different places, just run both SQL statements and store the results into 2 different Recordsets.
<%
set rs1=Server.CreateObject("ADODB.recordset")
set rs2=Server.CreateObject("ADODB.recordset")
SQL1="select * from TABLE where Category='test1' order by ID desc"
SQL2="select * from TABLE where Category='test2' order by ID desc"
rs1.Open SQL1, ConnectionString
rs2.Open SQL2, ConnectionString
Test1_ID = rs1("ID")
Test1_Data1 = rs1("Data1)
Test1_Data2 = rs1("Data2")
Test1_Category = rs1("Category")
Test2_ID = rs2("ID")
Test2_Data1 = rs2("Data1")
Test2_Data2 = rs2("Data2")
Test2_Category = rs2("Category")
%>
This code assumes you already have set your connection string = ConnectionString and have already opened it, and side note, you can share the connection, or you can make one for each recordset, if its not that much data it won't matter, but if its a larger chunk of data, it wouldn't hurt to have 2 connection objects.
Now when you go to display the results you can just rs1.MoveNext to move to the next record in the first set, and rs2.MoveNext to move to the next record in the second recordset. Your question was how to be able to display the records in different places, so now you have the variables names and a way to move to the next record, if you know how to move through a loop advancing the recordsets until you reach the last record, you just do that for both recordsets, and as each one is being done and displayed, they will be separate until you write the part to display the results from rs2 as well.
Closing Notes - In case you need to be specific in your SQL, you can specify the source in the format of database.tablename. So in the example you would do something like this for SQL1:
select * from DATABASE.TABLE where Category='test1' order by ID desc
Is it possible to create an SQL query to compare a field within a single table to see if a change has been made and if possible list the before and after?
I have the following SQL query written in Excel 2010 VBA, which connects to an Oracle PostGreSQL database
Dim au As String
au = "SELECT id, priority, flag, code " _
& "FROM hist WHERE ( aud_dt >= '18/05/2020' AND aud_dt <='18/05/2020' ) " _
Set rs = conn.Execute(au)
With ActiveSheet.QueryTables.Add(Connection:=rs, Destination:=Range("A1"))
.Refresh
End With
Where fields include:
"priority" is the field that I'd like to check for changes which will
be a single number between 0-9
"code" is the record that has been
assigned the priority and is a mixture of numbers and letters up to 7
characters
"flag" shows a 1 as the active record, and 2 as an edited
record
"id" refers to the user account
I'd ideally like to end up with something like: id | priority | flag | priority_old | flag_old | code
Which should show the before and after changes to the priority. If the record shows priority=3 and flag=2 and code=Ab12, there must also be record with a 1 flag, as that is now the active record. If it has the same priority number for the code I'm not interested in it as that just means something else was changed instead as I have not listed all the column fields.
If the active record now shows priority=4, flag=1 and code=Ab12, that would be exactly the record I need to see.
Consider a self-join query (possibly you need to adjust date filter in WHERE depending on when items change):
SELECT h1.id, h1.priority, h1.flag,
h2.priority AS priority_old, h2.flag AS flag_old, h1.code
FROM hist h1
LEFT JOIN hist h2
ON h1.code = h2.code
AND h1.priority <> h2.priority
AND h1.flag = 1
AND h2.flag <> 1
WHERE (aud_dt >= '2020-05-18' AND aud_dt <='2020-05-18')
I need to modify an old vb6 application that needs to import some data from a dBase IV database.
In the past the selection query involved a single table (dbf file) and used to work perfectly.
Now i need to edit this query to introduce a join on a second table, using multiple fields.
This is a simplified version of my code:
Dim cnn As New Connection
Dim rs As New Recordset
Dim sql As String
cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Extended properties=dBase IV;Data source=d:\100\db;"
sql = "..." 'see below!
rs.CursorLocation = adUseClient
rs.Open sql, cnn, adOpenDynamic, adLockOptimistic
debug.print rs.RecordCount
rs.Close
cnn.Close
These two tables have a typical master-detail structure; I checked the db documentation and inspected TABFAT01 and TABFAT02, so I can assume that:
Join between these tables is [TABFAT01] 1 <-> n [TABFAT02], and is done on
TIPDOC (text), ANNDOC (text) and NUMDOC (numeric) fields.
Each row in TABFAT01 has at least 1 joined row in TABFAT02.
Each row in TABFAT02 has 1 joined row in TABFAT02.
TABFAT01 has 63 records.
TABFAT02 has 907 records.
First issue
My first query is:
select t.TIPDOC, t.NUMDOC, t.ANNDOC, t.DATDOC, t.LIBER03, c.LIBER04
from TABFAT01 t inner join
TABFAT02 c on t.TIPDOC = c.TIPDOC and t.ANNDOC = c.ANNDOC and t.NUMDOC = c.NUMDOC
This query returns 0 records.
If I change conditions order this way:
select t.TIPDOC, t.NUMDOC, t.ANNDOC, t.DATDOC, t.LIBER03, c.LIBER04
from TABFAT01 t inner join
TABFAT02 c on t.ANNDOC = c.ANNDOC and t.NUMDOC = c.NUMDOC and t.TIPDOC = c.TIPDOC
the query returns 907 records.
I don't understand how and why conditions order has impact on query results.
Second issue
If I add a where clause:
select t.TIPDOC, t.NUMDOC, t.ANNDOC, t.DATDOC, t.LIBER03, c.LIBER04
from TABFAT01 t inner join
TABFAT02 c on t.ANNDOC = c.ANNDOC and t.NUMDOC = c.NUMDOC and t.TIPDOC = c.TIPDOC
where c.LIBER04 = 'a'
the query returns 0 records.
However, if I run this query:
select * from TABFAT02 c where LIBER04 = 'a'
it returns 1 record, with TIPDOC = 'F2', ANNDOC = '2018', NUMDOC = 1854.
A subsequent query:
select * from TABFAT01 t where t.TIPDOC = 'F2' and t.ANNDOC = '2018' and t.NUMDOC = 1854
returns 1 record, as expected.
This happens for every field I tried to put in where clause with joined tables, except TIPDOC.
If I filter by TIPDOC, results are correct.
Third issue
The first time I run my code after opening vb6 IDE it gives the following error:
Run-time error '-2147467259 (80004005)': Selected Collating sequence Not Supported by the operating system.
(I actually use the Italian version of vb6, and the original error message says "Sequenza di ordinamento selezionata non supportata dal sistema operativo.".
I guess that the message I written above is the right match for the English version.)
This error is not displayed on subsequent runs, until I close and reopen vb6.
I agree with thx1138v2 about not specifically using the Jet OleDB driver, but one more specific to the source. You mention dBASE IV. Is this accurate or is it really with Visual FoxPro. You could confirm if the table has indexes and note (memo) fields the file names would be suffixed as
YourTable.dbf (actual table)
YourTable.cdx (compound index file)
YourTable.fpt (notes/memo file content is table has such columns).
Having confirmed, and if it IS Visual Foxpro, I would get the Microsoft driver directly from Microsoft
If the data table is being updated from a source outside VFP, then it is possible that the indexes may be out of sync as the other drivers won't necessarily open the index file (cdx) correctly to keep the new records in synch. So when trying to do the join based on one series of join clauses it does use the non-synched index. But changing the order forces to default to the natural order, thus seeing all possible records to resolve.
See and hopefully this makes sense and helps to tune-in on the missing link in your querying issue.
First clear up your error message. "Selected Collating sequence Not Supported by the operating system." probably means you didn't specify a collating sequence or the Jet.OLEDB driver doesn't support whatever collating sequence you specified.
I never got the Jet.OLEDB drivers to work with my application. My guess is because the files I was dealing with were created by FoxPro. So I used the Visual Fox Pro ODBC drivers. Here's an example connection.
"DSN=BCLVariance;UID=;SourceDB=C:\AMSI\BCLTemp;SourceType=DBF;Exclusive=Yes;BackgroundFetch=No;Collate=Machine;Null=Yes;Deleted=Yes;"
You will see that it allows you to control various parameters that you aren't controlling in your Jet.OLEDB connection string. The ODBC data source is setup under System Data Sources tab in ODBC Data Source Administrator.
I never understood exactly why but the sequence you state the table names in a SQL query does make a difference. It has to do with how the internal query interpreter handles them. You need to specify the detail table first and the master is joined to the detail. Like this:
select t.TIPDOC, t.NUMDOC, t.ANNDOC, t.DATDOC, t.LIBER03, c.LIBER04
from TABFAT02 c inner join
TABFAT01 t on t.TIPDOC = c.TIPDOC and t.ANNDOC = c.ANNDOC and t.NUMDOC = c.NUMDOC
That may also clear up your other problems. Give it a try.
BTW, you'll also need to change your initial code to:
if Not rs.BOF then
rs.MoveLast
debug.print "rs.RecordCount=";rs.RecordCount
else
debug.print "rs.BOF = True"
end if
I have two tables that I need to update. I have to go through every record on one table then do some modifications to the data then upload the modified
data to another table!
The two tables have 3,000 records and 11,000 records. Plus I also have to
check some info from a third table with about 50 records!
Dim id
Dim fly_SQL
id="user1"
Dim rsc1_conn As ADODB.Connection
Set rsc1_conn = CreateObject("ADODB.Connection")
rsc1_conn.Provider = "SQLOLEDB"
rsc1_conn.ConnectionString = "SERVER=companyserver;UID=" & id &
";Trusted_Connection=Yes;DATABASE=DATAbank" '
rsc1_conn.Open
Set rsc1 = CurrentDb.OpenRecordset("SELECT * FROM main_database",
dbOpenDynaset, dbSeeChanges)
rsc1.movefirst
do until rsc1.EOF
fly_SQL = "Select * from alt_db where alt_db.number = main.net_number"
Set rsc2 = CurrentDb.OpenRecordset(fly_SQL)
do stuff
code = dlookup( "type_def", "third_rec" , alt_db.activity = activity)
The two tables both use net_number as a reference which on the main is primary key unique, but the alt_db has multiple entries.
So basically I have to loop through each net_number on the main, look at the matching net_number on the alt_db then compare an activity field
with a third table to see which field I update on the main! If it's a Project management expense I put it in the main.PM_cost. The net_number in alt_db might repeat for 10 other expenses that need to be funneled into their proper expense categories in the main DB! As an example:
Main table looks like
net_number
first record shows
main.netnumber = 123456
main.cont_cost
main.PM_cost
main.mgmt_cost
alt_db table looks like
alt_db.net_number
alt_db.activity
alt_db.PM_cost
alt_db.const_cost
alt_db.mgmt_cost
third_rec looks like
third_rec.code
third_type
where data can be something like con1 , sabb ,
code type
sauf construction
con1 management
I130 project management
And needed rules:
check alt_db.activity with third_rec.act and return activity type
If activity type is construction then I put the alt_db.cost into main.const_cost
If activity type is project_mgmt then I put the alt_db.cost into main.PM_cost. The alt_db.activity could be con1 or SAF4 and the type is determined by the third_rec table.
Trying to figure out the best (most efficient way) to go about this.
Any suggestions?
The above code will surely be missing proper variable definitions and such but it's just for explaining my dilemma!
I could probably do it with DLookup but I don't think that would be very efficient!
Pete
Best way was to build a query that produces a file filtered data from the Main and alt_db to group the activities by net_number . Then use a case to determine which fields from alt_db to update using the third file and update the result in the proper fields on the main db.
I have an Access database that will be required to present data with several one-to-many relationships on one row (e.g., it would list items as "a, b, e, f", and I would have multiple columns like that). I know it's a bad idea to store data that way as well, but considering that I'm allowing the user to filter on several of these columns, I can't think of a better way of dealing with the data than violating first normal form.
As an example: say that I have several journal articles, each of which may report on multiple animals and multiple vegetables. The user can filter on the source name, or they can filter on one or more animals and one or more vegetables. The output should look like
Source name....animals...............vegetables
Source 1.......dogs, cats, birds.....carrots, tomatoes
Source 2.......leopards, birds.......tomatoes, zucchini, apples
Source 3.......cats, goldfish........carrots, cucumbers
Typically you would have a separate table with Source name + animal:
Source name......animal
Source 1.........dog
Source 1.........cats
Source 1.........birds
Source 2.........leopards
etc
and a similar table for vegetables. But considering how the data needs to be presented to the user (a comma-separated list), and how the user filters the data (he may filter to only see sources that include dogs and cats, and sources with carrots and tomatoes), I think it makes sense to store the data as comma separated lists for animals and vegetables. With a comma-separated list, when the user selects multiple vegetables and multiple animals, I can say
WHERE (Vegetables like "*carrots*" and Vegetables like "*tomatoes*") AND (Animals like *dogs*" and Animals like "*cats*")
I can't think of an efficient way to do this same kind of query in Access without using a lot of VBA and multiple queries.
You can always construct a scenario in which violating any rule makes sense, so the answer to the question in your title is Yes.
This is not one of those scenarios, however. The searching and presentation issues you raise are common to most one-to-many relationships (or, at least, to many one-to-many relationships) and if this were a reason to violate first normal form then you wouldn't see a lot of normalized databases.
Construct the database correctly and you won't have to worry about commas, search terms embedded in each other, and slow searches due to the lack of indexes. Write a reusable piece of code to perform the comma-separate roll-ups for you so you don't keep reinventing the wheel.
i would still normalize this properly - and then worry about the presentation.
in Oracle - this would be done with a user defined aggregate function.
Why not contruct a Join Table so you can sustain the 1:1 in relationship to field referential integrity. Otherwise you will have to parse out the 1:many field value to then get a referential association so everything works magically (hehehe ;))
When i find myself in need to violate the 1st normal form, the answer 9:10 is to create a Join Table and construct some methodology to produce the desired effect.
Edited: 2012-10-09 9:06AM
This design was in response to an unknown amount of information to be displayed in an unknown amount of column/fields. Although mine is oriented towards numerical values, you could simply develop a vba method to concatenate the information fields to produce a singular field of data.
Table1
gid (Number) <- Table2.id
cid (Number) <- Table3.id
price (Number)
gid MANY <- ONE Table2.id
cid MANY <- ONE Table3.id
crt_CategoryPG
TRANSFORM Sum(Table1.price) AS SumOfprice
SELECT View1.tid, Table1.cid, View1.category
FROM Table2 INNER JOIN (View1 INNER JOIN Table1 ON View1.cid = Table1.cid) ON Table2.gid = Table1.gid
WHERE (((Table2.active)=True) AND ((View1.active)=True))
GROUP BY View1.tid, Table1.cid, View1.category, Table2.active, View1.active
ORDER BY View1.tid, Table1.cid
PIVOT [Table2].[gid] & " - " & [Table2].[nm];
Refresh the CT Table
Public Function RefreshCategoryPricing(Optional sql As String = "")
If HasValue(lstType) Then
Application.Echo False 'Turn off Screen Updating
Dim cttbl As String: cttbl = CreateCTTable("crt_CategoryPG") 'Create Table to store the Cross-Tab information
If IsNullOrEmpty(sql) Then
sql = SQLSelect(cttbl)
End If
Dim flds As DAO.Recordset: Set flds = CurrentDb.OpenRecordset(sql)
Dim fldwd As String, fldhd As String 'Store the Field Width pattern and Field Header Row
fldhd = "-1;-1;Category"
fldwd = "0"";0"";2.5""" 'Handles `tid`, `cid`, and `category` columns in the ListBox
'Assign the number of columns based on the number of fields in CTtable
lstCategoryPG.ColumnCount = flds.Fields.Count
Dim fld As Long
For fld = 3 To (flds.Fields.Count - 1)
fldwd = fldwd & ";.75"""
fldhd = fldhd & ";" & flds.Fields(fld).Name
Next
GC flds
lstCategoryPG.ColumnHeads = True
lstCategoryPG.ColumnWidths = fldwd
sql = SQLSelect(cttbl, , ("tid = " & lstType.Value))
lstCategoryPG.Enabled = True
RefreshControl CurrentDb, lstCategoryPG, sql, , False
lstCategoryPG.AddItem fldhd, 0
Application.Echo True 'Turn on Screen Updating
End If
End Function
Create Cross-Tab Table
'#ct - String value, Source Cross-Tab to base Table design off of
Public Function CreateCTTable(ct As String) As String
Dim tbl As String: tbl = "tbl_" & ct
Dim sql As String
If TableExists(tbl) Then
'Table exists and needs to be dropped
sql = SQLDrop(tbl)
CurrentDb.Execute sql
End If
'Create Table
sql = SQLSelect(ct, "* INTO " & tbl)
CurrentDb.Execute sql
CreateCTTable = tbl
End Function