Avoiding Duplicates when appending records in Access - sql

I am aware this has been asked multiple times, but for one reason or another the solutions are not working for me.
Database Layout:
I have Table1 (Scanner_Location) Who is getting data pulled from another table/ subform on a form (Scanner IBOB) * Holds Columns: FP#, Count, Location, Model_ID, PK-SL_ID
Table2 (Scanner Detail) Holds Two of the three data columns: (FP#, Location PK-SN)
Table3 (Scanner_Model) Holds the last data column, displayed in a subform. (PK-Model_ID)
The user will input FP#, and location in one section of the form, then navigate to the subform, and select multiple Models, and enter the count (Textbox). Once Selected, they click an 'update' button that executes my queries. (Of which I have an update, AND an Append Query)
The problem is, just using an update query doesn't add the records. And using an Append query creates duplicates of the existing data.
Here's how the flow carries out:
User selects Model 1 and Model 2 with a count of 4 and an FP# of 100. Clicks update.
The queries update, and the information enters correctly.
User Selects the same models again (Model_Select), with the same FP# and count, the Table1 has the same information entered again, with a different primary key.
The goal:
The append query creates duplicates of existing data. I only want my update and/or append queries to:
Update the existing data - Looking for anything with the same FP#
Add any records that do not exist already (Looking at Model_ID and FP#)
INSERT INTO Scanner_Location ( Model_ID, FootPrints_Num, Location_ID, Scanner_Loc_Cnt )
SELECT Scanner_Model.Model_ID, [Forms]![Scanner_IBOB]![fpNum_txt] AS [FP#],
[Forms]![Scanner_IBOB]![Location_Cbo_main] AS Location,
[Forms]![Scanner_IBOB]![Scanner_Loc_CntTxt] AS [Count]
FROM Scanner_Detail
RIGHT JOIN Scanner_Model ON Scanner_Detail.Model_ID = Scanner_Model.Model_ID
WHERE (((Scanner_Model.SM_Acc_Select)=True)
AND ((NOT Exists (SELECT * FROM Scanner_location
WHERE (((Forms!Scanner_IBOB!fpNum_txt)=Forms!Scanner_IBOB!fpNum_tx‌​t)
And ((Scanner_Model.SM_Acc_Select)=True)); ))=False));

No query named 'Update_SLoc_Acc53' - there are 'Update_SLoc_Acc3' and 'Update_SLoc_Acc54'. I modified 'Update_SLoc_Acc54' because it is the one called by the code.
The query was not pulling the Location_ID from the combobox. I found the Bound Column was set to 1 and should be 0 to reference the Location_ID column because column index begins with 0. Can hide this column from user by setting width to 0.
This query seems to work:
INSERT INTO Scanner_Location ( Model_ID, FootPrints_Num, Location_ID, Scanner_Loc_Cnt )
SELECT Scanner_Model.Model_ID, [Forms]![Scanner_IBOB]![fpNum_txt] AS FPNum,
[Forms]![Scanner_IBOB]![Location_Cbo_main] AS Location,
[Forms]![Scanner_IBOB]![Scanner_Loc_CntTxt] AS CountMod
FROM Scanner_Model
WHERE (((Scanner_Model.SM_Acc_Select)<>False)
AND (([Model_ID] & [Forms]![Scanner_IBOB]![fpNum_txt] &
[Forms]![Scanner_IBOB]![Location_Cbo_main])
NOT IN (SELECT Model_ID & Footprints_Num & Location_ID FROM Scanner_Location)));
Note I did not use # in field name. Advise not to use punctuation/special characters in names with only exception of underscore. Also used CountMod instead of Count as field name.
Why the requirement to select two models? What if one is added and the other isn't?
I have concerns about the db structure.
Don't think App_Location and App_Detail should both be linking to other tables. Why is Location_ID the primary key in App_Location as well as primary key in Location_Data? This is a 1-to-1 relationship.
Is Serial_Number the serial number for scanner? Why is it a primary key in Telnet? This also results in a 1-to-1 relationship in which case might as well combine them.
If an app is associated with a scanner and scanner is associated with a location then don't need location associated with app. Same goes for scanner and telnet.
Scanner_Location table is not linked to anything. If purpose of this table is to track a count of models/footprints/locations -- as already advised this is usually not a good idea. Ideally, count data should be calculated by aggregate query of raw data records when the information is needed.

Maybe use NOT IN, something like:
[some identifier field] NOT IN (SELECT [some identifier field] FROM
Review EXISTS vs IN

Consider following adjusted append query that checks existence of matched Model_ID and FP_Num in Scanner_Location. If matches do not exist, then query imports selected records as they would be new records and not duplicates. Also, table aliases are used for readability and subquery correlation.
INSERT INTO Scanner_Location ( Model_ID, FootPrints_Num, Location_ID, Scanner_Loc_Cnt )
SELECT m.Model_ID, [Forms]![Scanner_IBOB]![fpNum_txt] AS [FP#],
[Forms]![Scanner_IBOB]![Location_Cbo_main] AS Location,
[Forms]![Scanner_IBOB]![Scanner_Loc_CntTxt] AS [Count]
FROM Scanner_Detail d
RIGHT JOIN Scanner_Model m ON d.Model_ID = m.Model_ID
WHERE ((m.SM_Acc_Select = True)
AND (NOT EXISTS (SELECT 1 FROM Scanner_Location loc
WHERE ((loc.FootPrints_Num = Forms!Scanner_IBOB!fpNum_tx‌​t)
AND (loc.Model_ID = m.Model_ID)) ) ));

Related

PHP/ Laravel application- query two database tables that are related through a third table? Inner join

I'm working on a Laravel/ Angular application, and have a form on one of pages displaying a table with information regarding a number of accounts. One of the columns in the table is titled 'Contact', and each cell in the column displays the name of the default contact for that table entry.
The cells in this column also display an 'Edit' button, which will open a dialog when clicked to allow the user to add another contact to that table entry- the dialog displays a form with the fields 'first name' 'surname' & 'email', and has an 'Add Contact' button.
There is also a short 'preview' of how the name should appear displayed in a letter generated to send to that user, i.e "Hi forename surname" or "Hi forename", depending on the the type of contact, and the letter template being used.
I'm having a bit of trouble getting the relevant name to be displayed, because I'm not sure how to write the SQL query I'll need to retrieve the relevant name.
The tables are set up such that the account.account table contains the values for:
'account ID', 'account name', account type ID',
and the account.property table contains the values for
'property ID', property label', 'property tag', 'property description'.
In the account.property table, there are two rows which hold the names to be used for different methods of communication- one with the 'property tag' addresseename, and the other with the 'property tag' addresseenamepdf. I want to display these values in the 'preview' on the dialog, but I'm not sure how to write the SQL to retrieve them.
As far as I can see, there is no primary/ foreign key relationship between the tables, but I have been told that they are linked using the View account.vAccount. It's been several years since I've done anything more than very basic database programming with SQL, and I haven't come across views before, so am unsure how to use them in a query...
In MS SQL SMS, if I right-click the `account.vAccount view, and select the top 1000 rows, I see that the query:
SELECT TOP (1000) [accountId]
,[name]
,[typeId]
,[accountType]
,[accountTypeTag]
,[typeParentId]
,[parentAccountType]
,[parentAccountTypeName]
,[balanceDate]
, // several other values here
FROM [myDB].[account].[vAccount]
is run, and am presented with a list of the results, which I can filter by adding a where clause to the end of the query:
FROM [myDB].[account].[vAccount] where accountId = 53092;
and this just returns the view row for that particular account. However, I can't see the addresseename & addresseenamepdf columns whose data I want to retrieve from the account.property table.
My (limited) understanding of SQL tells me that I will need to create a relationship between these two tables, in order to be able to query the addressename & addresseenamepdf values that I want using just the accountId value (i.e. have a foreign key from one of the tables in the other table).
Is my understanding correct here, or can I use the view to retrieve this data without creating a relationship between the tables? If I can, how would I do this?
--Edit--
So it seems I'd missed that these two tables are actually related through a third table (I've only just started working on this application)- the tables are:
account
accountID (PK)
name
typeId
...
propertyValue
propertyValueId (PK)
accountId
propertyId
...
property
propertyId (PK)
propertyLabel
propertyTag
...
The tables have the relationships:
account one-to-many propertyValue many-to-one property
How would I write a query that would return two particular properties (the properties whose propertyId values are 48 & 49 for an account where the accountId value is 53092?
I've tried running the query:
SELECT TOP (1000) [accountID]
,[name
...
FROM [myDB][account].[account] where accountID = 53092
INNER JOIN [myDB].[account].[propertyValue] where propertyTag = 'ADDRESSEENAME' | propertyTag = 'ADDRESSEENAMEPDF';
but when I try to execute, I get an error that says:
Incorrect syntax near the keyword 'INNER'.
Why am I getting this? What am I doing wrong?
Structure of the query should be
Select
From
join on
where
group by
Having.
You have the where clause, before the join clause.
Change to:
SELECT TOP (1000) [accountID]
,[name
...
FROM [myDB][account].[account]
INNER JOIN [myDB].[account].[propertyValue] ON … (you are missing the on clause)
where accountID = 53092 and (
propertyTag = 'ADDRESSEENAME' | propertyTag = 'ADDRESSEENAMEPDF');

SQL - Append counter to recurring value in query output

I am in the process of creating an organizational charts for my company, and to create the chart, the data must have a unique role identifier, and a unique 'reports to role' identifier for each line. Unfortunately my data is not playing ball and it out of my scope to change the source.
I have two source tables, simplified in the image below. It is important to note a couple of things in the data.
An employees manager in the query needs to come from the [EmpData] table. The 'ReportsTo' field is only in the [Role] table to be used when a role is vacant
Any number of employees can hold the same role, but for simplicity lets assume that there will only ever be one person in the 'Reports to' role
Using this sample data, my query is as follows:
/**Join Role table with employee data table.
/**Right join so roles with more than one employee will generate a row each
SELECT [Role].RoleId As PositionId
,[EmpData].ReportsToRole As ReportsToPosition
,[Role].RoleTitle
,[Empdata].EmployeeName
FROM [Role]
RIGHT JOIN [EmpData] ON [Role].RoleId=[EmpData].[Role]
UNION
/** Output all roles that do not have a holder, 'VACANT' in employee name.
SELECT [Role].RoleId
,[Role].ReportsToRole
,[Role].RoleTitle
,'VACANT'
FROM [Role]
WHERE [Role].RoleID NOT IN (SELECT RoleID from [empdata])
This almost creates the intended output, but each operator roles has 'OPER', in the PositionId column.
For the charting software to work, each position must have a unique identifier.
Any thoughts on how to achieve this outcome? I'm specifically chasing the appended -01, -02, -03 etc. highlighted yellow in the Desired Query Output.
If you are using T-SQL, you should look into using the ROW_NUMBER operator with the PARTITON BY command and combining the column with your existing column.
Specifically, you would add a column to your select of ROW_NUMBER () OVER (PARTITION BY PositionID ORDER BY ReportsToPosition,EmployeeName) AS SeqNum
I would add that to your first query, and then, in your second, I would do something like SELECT PositionID + CASE SeqNum WHEN 1 THEN "" ELSE "-"+CAST(SeqNum AS VarChar(100)),...
There are multiple ways to do this, but this will leave out the individual ones that don't need a "-1" and only add it to the rest. The major difference between this and your scheme is it doesn't contain the "0" pad on the left, which is easy to do, nor would the first "OPER" be "OPER-1", they would simply be "OPER", but this can also be worked around.
Hopefully this gets you what you need!

MS Access - Linking to record through stored ID

I have a few tables set up in Access as follows (forgive the slightly redundant example content):
Table 1:
- ID
- FirstName
- SecondName
Table 2:
- ID
- Details
- PersonID -> Table 1[ID]
Table 3:
- ID
- Group
- PersonDetails -> Table 2[ID]
Table 1 is the base table containing records and retrieving no other information. For example, it could store someone's first and second names, along with an autonumber ID.
Table 2 contains records which, amongst other things, contain a field that links to Table 1 and stores the ID of one of the records held there. With the lookup wizard I can choose to utilise all fields from Table 1, store the ID of the Table 1 record in the Table 2 field and also display the first and second names in the combobox on the form to make choosing a record more intuitive.
In table 3, I need to store the ID of one of the records in Table 2. However, I would also like to again display in the form combobox the first and second names from the related record (in Table 1) whose ID is stored in Table 2. I can't choose to utilise, for example, the PersonDetails field from table 2 as this just puts ID numbers into the combobox - I'd need to do something equivalent of:
Table 2[ID]->[FirstName]
Is this possible to do with the lookup wizard in Access or would I have to look into queries or VBA?
Thanks.
Your query for your combo should look something like this:
SELECT cp.ID, cp.ReferenceName, c.Company, p.FeePerHour
FROM (ClientProfile AS cp LEFT JOIN Clients AS c ON cp.ClientID = c.ID)
LEFT JOIN Pricing AS p ON cp.PricePlanID = p.ID;
The main problem with your query is that you're missing the Parenthesis that are needed when you have multiple joins. If you had another join, you'd need a second set of parenthesis.
I took some liberty and used table aliases. It makes SQL concise and more readable.
If this query still doesn't work it might be because you're trying to join "child tables" to the "main table" without using your Foreign Key fields as the joining field. If this were my database the SQL would look something like this:
SELECT cp.ClientProfileID, cp.ReferenceName, c.Company, p.FeePerHour
FROM (ClientProfile AS cp LEFT JOIN Clients as C ON cp.ClientID = c.ClientID)
LEFT JOIN Pricing AS p ON cp.ClientProfileID = p.ClientProfileID;
Personally, I basically never use just plain ID as a field name. And when creating a foreign key I usually use the same field name as what the original table has. There are exceptions, especially in the case where you need to link back to the same table more than once. Consider the case where you are tracking real estate properties and you have a Buyer and a Seller that are both entities (but different from each other) in the same People table. You would then need to name them something like BuyerID and SellerID but ultimately they would link back to the PersonID in the Person table.
So Here's the table design I would go for. Notice I changed Group to PriceGroup. Watch out for reserved words when programming, not only in SQL but any language. If you name your field Group at some point you'll be trying to "Group on Group" which sounds and looks bad, if nothing else.
Client:
- ClientID
- FirstName
- SecondName
ClientProfile:
- ClientProfileID
- Details
- ClientID (Foreign Key to Client)
Pricing:
- PricingID
- PriceGroup
- ClientProfileID (Foreign Key to ClientProfile)

SELECT DISTINCT. Please explain?

Wondering if someone could please explain the difference between these two queries and advise why one works and the other doesn't.
This one works. Gives me two records of the distinct GantryRtn value and their corresponding SSD value.
SELECT DISTINCT GantryRtn as Gantry, ROUND(Field.SSD,1) as SSD
FROM Field, PlanSetup, Course, Patient, Radiation
WHERE Field.RadiationSer=Radiation.RadiationSer
AND Radiation.PlanSetupSer=PlanSetup.PlanSetupSer
AND PlanSetup.CourseSer=Course.CourseSer
AND Course.PatientSer=Patient.PatientSer
AND Patient.PatientId='ZZZ456'
AND PlanSetup.PlanSetupId='F T1 R CHEST'
However there is a foreign key in the Field table that links to the primary key of another table that contains a plain text name for each field. I'd also like to extract that name (in a separate query if I have to) by pulling out this foreign key RadiationSer. But as soon as I put RadiationSer into the query, I lose my DISTINCT result.
SELECT DISTINCT GantryRtn as Gantry, ROUND(Field.SSD,1) as SSD, Field.RadiationSer
FROM Field, PlanSetup, Course, Patient, Radiation
WHERE Field.RadiationSer=Radiation.RadiationSer
AND Radiation.PlanSetupSer=PlanSetup.PlanSetupSer
AND PlanSetup.CourseSer=Course.CourseSer
AND Course.PatientSer=Patient.PatientSer
AND Patient.PatientId='ZZZ456'
AND PlanSetup.PlanSetupId='F T1 R CHEST'
This second query gives me 7 records with non-distinct GantryRtn values.
Why does this happen??
I have investigated using GROUP BY but this slows the query down and appears to pull ALL GantryRtn's out of the database (100s of records).
Thanks
Greg
The DISTINCT keyword applys to a result set (all fields) and not just to the first field.
In your case:
SELECT DISTINCT GantryRtn as Gantry, ROUND(Field.SSD,1) as SSD, Field.RadiationSer
will return any records that are distinct (not the same) when taken together with Gantry, SSD, and RadiationSer
So, you may have 7 records for the same Gantry and with different values for RadiationSer.
If you'd like to first filter by distinct Gantry values you can accomplish that with a sub-query and an inner join but somehow you must settle on which RadiationSer value to use.

Update values in each row based on foreign_key value

Downloads table:
id (primary key)
user_id
item_id
created_at
updated_at
The user_id and item_id in this case are both incorrect, however, they're properly stored in the users and items table, respectively (import_id for in each table). Here's what I'm trying to script:
downloads.each do |download|
user = User.find_by_import_id(download.user_id)
item = item.find_by_import_id(download.item_id)
if user && item
download.update_attributes(:user_id => user.id, :item.id => item.id)
end
end
So,
look up the user and item based on
their respective "import_id"'s. Then
update those values in the download record
This takes forever with a ton of rows. Anyway to do this in SQL?
If I understand you correctly, you simply need to add two sub-querys in your SELECT statement to lookup the correct IDs. For example:
SELECT id,
(SELECT correct_id FROM User WHERE import_id=user_id) AS UserID,
(SELECT correct_id FROM Item WHERE import_id=item_id) AS ItemID,
created_at,
updated_at
FROM Downloads
This will translate your incorrect user_ids to whatever ID you want to come from the User table and it will do the same for your item_ids. The information coming from SQL will now be correct.
If, however, you want to update the tables with the correct information, you could write this like so:
UPDATE Downloads
SET user_id = User.user_id,
item_id = Item.item_id
FROM Downloads
INNER JOIN User ON Downloads.user_id = User.import_id
INNER JOIN Item ON Downloads.item_id = Item.import_id
WHERE ...
Make sure to put something in the WHERE clause so you don't update every record in the Downloads table (unless that is the plan). I rewrote the above statement to be a bit more optimized since the original version had two SELECT statements per row, which is a bit intense.
Edit:
Since this is PostgreSQL, you can't have the table name in both the UPDATE and the FROM section. Instead, the tables in the FROM section are joined to the table being updated. Here is a quote about this from the PostgreSQL website:
When a FROM clause is present, what essentially happens is that the target table is joined to the tables mentioned in the fromlist, and each output row of the join represents an update operation for the target table. When using FROM you should ensure that the join produces at most one output row for each row to be modified. In other words, a target row shouldn't join to more than one row from the other table(s). If it does, then only one of the join rows will be used to update the target row, but which one will be used is not readily predictable.
http://www.postgresql.org/docs/8.1/static/sql-update.html
With this in mind, here is an example that I think should work (can't test it, sorry):
UPDATE Downloads
SET user_id = User.user_id,
item_id = Item.item_id
FROM User, Item
WHERE Downloads.user_id = User.import_id AND
Downloads.item_id = Item.import_id
That is the basic idea. Don't forget you will still need to add extra criteria to the WHERE section to limit the rows that are updated.
i'm totally guessing from your question, but you have some kind of lookup table that will match an import user_id with the real user_id, and similarly from items. i.e. the assumption is your line of code:
User.find_by_import_id(download.user_id)
hits the database to do the lookup. the import_users / import_items tables are just the names i've given to the lookup tables to do this.
UPDATE downloads
SET downloads.user_id = users.user_id
, downloads.item_id = items.items_id
FROM downloads
INNER JOIN import_users ON downloads.user_id = import_users.import_user_id
INNER JOIN import_items ON downloads.item_id = import_items.import_item_id
Either way (lookup is in DB, or it's derived from code), would it not just be easier to insert the information correctly in the first place? this would mean you can't have any FK's on your table since sometimes they point to one table, and others they point to another. seems a bit odd.