SQL: check one table and enter values into another - sql

I have a very simple MS Access User Table (USER_TABLE) consisting of 3 fields: Customer_Number, User_Name, and Email_Address. I have another table (NEW_USERS) that consist of new requests for Users. It has a User_Status field that is blank by default, and also has the Customer_Number, User_Name, and Email_Address fields.
Half of the new requests that come through are users already existing, so I want to set up a query that will check the USER_TABLE to determine if a new request exists or not, using the Email_Address field checked vs. the Customer_Number field. Complicating this is the fact that 1) Customer_Number is not unique (many Users exists for a single Customer Number) and 2) Users can have multiple accounts for different Customer Numbers. This results in 4 scenarios in the NEW_USERS table when checking vs. the USER_TABLE:
Email_Address does not exist for Customer Number in USER_TABLE (New)
Email_Address exists for Customer Number in USER_TABLE (Existing)
Email_Address does not exist for Customer Number in USER_TABLE, but exists for other Customer Numbers (New-Multi)
Email_Address does exist for Customer Number in USER_TABLE, and also exists for other Customer Numbers (Existing-Multi)
What I would like to do is run these checks and enter the corresponding result (New, Existing, New-Multi or Existing-Multi) into the User_Status field.
This seems like it would be possible. Is it possible to run 4 separate queries to make the updates to NEW_USERS.User_Status?

When you're working in Access, you really need a field that uniquely identifies each record. At the very least, some combination of field, like customerid and email.
Beyond that, since you have a few criteria to satisfy, the easiest way is probably to make a single select statement that compares data between the results of multiple select statements. Look into outer joins for picking the results from one table that are not found in another. Something like -
insert into user_table select customerid, email_address from
(select customerid, email_address from new_users inner join user_table on ...) as expr1,
(select customerid, email_address from new_users outer join user_table on ...) as expr2
where expr1.customerid = expr2.customerid and expr1.new_users = expr2.new_users
I recommend trying out the free stanford course on sql, theres a handy lesson on nesting your select statements - its a good way to get results that fit a lot of criteria. http://class2go.stanford.edu/
As an aside, they do a lot using syntax of 'specifying joins in the where claus' which is increasingly frowned upon, but much easier to understand.

Related

How to make all rows in a table identical which the exception of 1 field?

I am trying to make it so all the users have the same items because I am doing an experiment with my app and need the experimental control of flattened data.
I used the following SQL statement in my last attempt:
insert into has (email,id,qty,price,item_info,image)
select 'b#localhost.com',id,qty,price,item_info,image
from
(
select * from has
where email != 'b#localhost.com'
) as o
where o.id not in
(
select id from has
where email = 'b#localhost.com'
);
This should add all items which 'b#localhost.com' does not already have but other users do have, to 'b#localhost.com's inventory. (the 'has' table)
However, I get the following error:
The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index
I understand what this error means, but I do not understand why it is occurring. My statement inserts all records which that email doesn't already have, so there should not be any duplicate id/email pairs.
The database structure is shown below, circles are attributes, squares are tables, and diamonds are relationship sets. The HAS table is a relationship set on USER_ACCOUNT and ITEM, where the primary keys are email and id respectively.
Please try the following...
INSERT INTO has ( email,
id,
qty,
price,
item_info,
image )
SELECT a.email,
b.id,
a.qty,
a.price,
a.item_info,
a.image
FROM has a
CROSS JOIN has b
JOIN
(
SELECT email,
id
FROM has
) c ON a.email = c.email AND
b.id <> c.id;
The CROSS JOIN appends a copy of each row of has to each row of has. Please see http://www.w3resource.com/sql/joins/cross-join.php for more information on CROSS JOIN's.
Each row of the resulting dataset will have two id fields, two email fields, two qty fields, etc. By giving each instance of has an alias we create the fields a.id, b.id, a.email, etc.
We then compare each combination of "old" email address and "new" id to a list of existing email / id combinations and insert the old values with the new id replacing the old one into has
If you have any questions or comments, then please feel free to post a Comment accordingly.
Further Reading
http://www.w3resource.com/sql/joins/cross-join.php for more information on CROSS JOIN's
https://www.w3schools.com/sql/sql_in.asp for more information on WHERE's IN operator
https://www.w3schools.com/sql/sql_groupby.asp for more information on GROUP BY
I think the issue here is not that the code is trying to insert something which already exists, but that it's trying to insert more than one thing with the same PK. In lieu of an response to my comment, here is one way to get around the issue:
INSERT INTO has (email,id,qty,price,item_info,image)
SELECT
'b#localhost.com',
source.id,
source.qty,
source.price,
source.item_info,
source.image
FROM
(
SELECT email, id, qyt, price, item_info, image FROM has
) as source
JOIN
(
SELECT min(email) as min_email, id FROM has GROUP BY by id)
) as filter ON
filter.min_email = source.email
WHERE
source.id not in
(
SELECT id from has WHERE email = 'b#localhost.com'
);
The key difference from your original code is my extra join to the subquery I've aliased as filter. This limits you to inserting the has details from a single email per id. There are other ways to do the same, but I figured that this would be a safe bet for being supported by Derby.
I removed the WHERE clause from the source sub-query as that is handled by the final WHERE.

Avoiding Duplicates when appending records in Access

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)) ) ));

Showing results from multiple tables

I'm working on a legacy Access CRM, for which I have been tasked to add some extra functionality to last us until we can switch to a proper solution.
I have a series of tables representing a one-to-many connection between a Case and various types of Case Notes. These case notes all represent various types of infomation specific to a given workflow (ie an email going into our database needs to have specific fields, which are different than a call), and therefore each different kind of Case Note is represented by a different table. So my table looks something like
Cases
ID
Title
etc...
Case_Emails
ID
Case ID
To Email
From Email
etc...
Case_Calls
ID
Case ID
Caller Email
etc...
Where there are some shared fields between Case Notes, such as Email. One desired feature is to filter a list of email addresses into a ComboBox, based on previous notes tied to a case along with defaults (email addresses used commonly within our day-to-day operations).
Currently, I am using VBA to open a query over each table, then I iterate over the RecordSet capturing unique values. However I would like to pull these from the same query, if possible.
I have been reading on the various type of JOINs, and I do not believe they do what I need.
Can I combine certain fields from different tables to create a list of unique values?
If you're just after a list of emails that occur at least once in at least one of the tables, then this should do the trick.
SELECT t1.emailAdd from
(
select emailField as emailAdd from Case_Emails
union
select emailField as emailAdd from Case_Calls
) t1
group by t1.emailAdd
order by t1.emailAdd
If it must only be emails that occur at least once in at least one of the tables and must be linked to a specific case (for example 12345), then this should do that:
SELECT t1.emailAdd from
(
select emailField as emailAdd from Case_Emails where CaseID=12345
union
select emailField as emailAdd from Case_Calls where CaseID=12345
) t1
group by t1.emailAdd
order by t1.emailAdd
I would like to pull these from the same query, if possible.
Using SQL DLL while in ANSI-92 Query Mode (or do the same manually using the Access GUI tools):
CREATE VIEW CaseEmailAddresses ( case_id, email_address )
AS
SELECT [Case ID], [To Email] FROM Case_Emails
UNION
SELECT [Case ID], [Caller Email] FROM Case_Calls;
Then in SQL DML (queries):
SELECT email_address
FROM CaseEmailAddresses
WHERE case_id = <inject id here>;
Or while you're in ANSI-92 Query Mode, you can create a stored procedure e.g. assuming case_id is type integer:
CREATE PROCEDURE GetEmailAddresses ( :case_id INT )
AS
SELECT case_id, email_address
FROM CaseEmailAddresses
WHERE case_id = :case_id;
Then you can use the technology of your choice to execute this proc while passing the parameter value.

SQL query with loop

I am having trouble with writing a SQL query in Access with foreign keys. There are two tables, 'Customers'(ID, Name, Surname) and 'Orders'(ID, Customer, Date, Volume). ID fields are primary, and Orders.Customer is a foreign key linked to Customers.ID, so a customer can have many orders or none.
My goal is to do a search on customers based on many criteria, one of which being if customers have at least an order which volume is superior to a certain quantity. I tried joins with SELECT DISTINCT but it still gives me duplicate results, plus I had to create an empty order for every customer without orders if the query didn't use the above condition.
Does anyone have an idea about that? Maybe some special instruction on foreign keys or 2 separate queries?
Based on the information you give, i only can give you hints on what I think you're doing/understanding wrong :
SELECT DISTINCT does select you a unique record, not a unique value, so if your statement selects all fields (*), distinct won't help you much there.
My guess is you had to create an empty order for each customer because you used INNER JOIN, try LEFT OUTER JOIN instead
For example :
SELECT DISTINCT Customers.*
FROM Customers
LEFT OUTER JOIN Orders
ON (Orders.Customer = Customers.id)
WHERE Volume > put_your_value

sql query with "with and in clause"

i have a table which store user name, hobby and city .hobby field contain different hobby joined using "," operator eg swimming, basket, cricket. I want to search user name who match at least one hobby according to my search criteria.
You should not have multiple attributes in one column. That's one of the number one rules of 3nf database design. Now you have to figure out ways to parse this data. This issue only gets worse and worse each and every day. Seperate the hobbies as multiple rows in your database.
I agree with #JonH that there shouldn't be more than one piece of information in a column. It stops the row being truly atomic.
But you are where you are, and you can use the LIKE clause to return rows that match a substring within a column.
Something like:
select hobbycolumn from hobbytable where hobbycolumn like '%swimming%'
for example
To do this properly you need to restructure your tables if possible. For what you are looking for a possible way would be to have 3 tables. I'm not sure who the city belongs to, so I put it with the user.
1 for user with the following cols:
id
name
city
A table for for hobbies:
id
name
And a user_hobbies join table that allows each user to have multiple hobbies, and each hobby to have multiple users:
id
user_id (foreign key)
hobby_id (foreign key)
Then searching for a user with a certain hobby is:
SELECT user.id, user.name FROM user
INNER JOIN 'user_hobbies' on user_hobbies.user_id=user.id
INNER JOIN 'hobbies' on hobbies.id = user_hobbies.hobby_id
WHERE hobbies.name LIKE "query";