Showing results from multiple tables - sql

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.

Related

MS Access SQL Query merging two different fields from separate tables shows reference ID's instead of actual values

I have two separate tables in my access database, which both use a third table as the reference for one particular field on each table. The data is entered onto the different tables by separate forms. Then I have several queries that then reference those particular fields that count and show unique values. Those queries show the actual values, then I created an sql query that does the same thing, only it shows the reference ID instead of the value in the actual field.
table ODI----------table CDN----------reference table
id RHA---------id CHA----------------id HA
1 blank----------1 radio---------------1 internet
2 internet-------2 tv------------------2 radio
3 referral-------3 radio---------------3 referral
4 tv-------------4 blank---------------4 repeat customer
5 blank----------5 internet------------5 tv
6 internet-------6 referral------------6 employee
7 referral-------7 referral------------7 social media
this is the code I am trying to make work.
SELECT m.[Marketing Results], Count(*) AS [Count]
FROM (SELECT RHA as [Marketing Results] FROM ODI
UNION ALL
SELECT CHA as [Marketing Results] FROM CDN) AS m
GROUP BY m.[Marketing Results]
HAVING (((m.[Marketing Results]) Is Not Null))
ORDER BY Count(*) DESC;
and what my desired result is,
Marketing Results--Counts
referral------------------4
internet------------------3
radio---------------------2
tv------------------------2
Lookup fields with alias don't show what is actually stored in table. ID is stored, not descriptive alias. Lookup alias will carry into regular queries but Union query only pulls actual stored values. At some point need to include reference table in query by joining on key fields in order to retrieve descriptive alias. Options:
in each UNION query SELECT line, join tables
join UNION query to reference table
join aggregate query to reference table
Most experienced developers will not build lookups in table because of confusion they cause. Also, they are not portable to other database platforms. http://access.mvps.org/Access/lookupfields.htm

SOQL, Salesforce - Join Two objects with two where clauses

I have two objects named as 'Opportunity' and 'Account.
I need to select ID, OpportunityName,CreatedDate from Opportunity table and AccountName, AccountCat from Account table where Account.OpportunityID = Opportunity.ID and AccountCat = 'DC'.
I refer this tutorial and executed in workbench, but didn't work.
Following is the query I used.
SELECT AccountName, AccountCat (SELECT Id, OpportunityName,CreatedDate FROM Opportunity) FROM Account WHERE OpportunityID
IN (SELECT Id FROM Opportunity) AND AccountCat = 'DC'
Run your query on the Opportunity object and then use reference fields to select fields from the parent object.
SELECT Id
, Name
, CreatedDate
, Account.Name
FROM Opportunity
WHERE Account.Name = 'GenePoint'
AccountCat is not a standard field, so I assume it's a custom field, in which case you need to use the developer name, probably something like Account.Cat__c or Account.Category__c or something like that.
SOQL is superficially similar to SQL, but there are significant differences, one of them being how records are joined.

How to tightly contain an SQL query result

I'm writing an application that implements a message system through a 'memos' table in a database. The table has several fields that look like this:
id, date_sent, subject, senderid, recipients,message, status
When someone sends a new memo, it will be entered into the memos table. A memo can be sent to multiple people at the same time and the recipients userid's will be inserted into the 'recipients' field as comma separated values.
It would seem that an SQL query like this would work to see if a specific userid is included in a memo:
SELECT * FROM memos WHERE recipients LIKE %15%
But I'm not sure this is the right solution. If I use the SQL statement above, won't that return everything that "contains" 15? For example, using the above statement, user 15, 1550, 1564, 2015, would all be included in the result set (and those users might not actually be on the recipient list).
What is the best way to resolve this so that ONLY the user 15 is pulled in if they are in the recipient field instead of everything containing a 15? Am I misunderstanding the LIKE statement?
I think you would be better off having your recipients as a child table of the memos table. So your memo's table has a memo ID which is referenced by the child table as
MemoRecipients
-----
MemoRecipientId INT PRIMARY KEY, IDENTITY,
MemoId INT FK to memos NOT NULL
UserId INT NOT NULL
for querying specific memos from a user you would do something like
SELECT *
FROM MEMOS m
INNER JOIN memoRecipients mr on m.Id = mr.memoId
WHERE userId = 15
No, you aren't misunderstood, that's how LIKE works.. But to achieve what you want, it would be better not to combine the recipients into 1 field. Instead try to create separate table that saves the recipient list for each memo..
For me I will use below schema, for your need:
Table_Memo
id, date_sent, subject, senderid, message, status
Table_Recipient
id_memo FK Table_Memo(id), recipient
By doing so, if you want to get specific recipients from a memo, you can do such query:
SELECT a.* FROM Table_Memo a, Table_Recipient b
WHERE a.id = "memo_id" AND a.id = b.id_memo AND b.recipient LIKE %15%
I am not sure how your application is exactly pulling these messages, but I imagine that better way would be creating a table message_recepient, which will represent many-to-many relationship between recipients and memos
id, memoId, recepientId
Then your application could pull messages like this
SELECT m.*
FROM memos m inner join message_recepient mr on m.id = mr.memoId
WHERE recepientId = 15
This way you will get messages for the specific user. Again, don't know what your status field is for but if this is for new/read/unread, you could add in your where
and m.status = 'new'
Order by date_set desc
This way you could just accumulate messages, those that are new

SQL: check one table and enter values into another

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.

distinct sql query

I have a simple table with just name and email called name_email.
I am trying to fetch data out of it so that:
If two rows have the same name, but one has an email which is ending with ‘#yahoo.com’ and the other has a different email, then the one with the ‘#yahoo.com’ email should be discarded.
what would be best way to get this data out?
Okay, I'm not going to get involved in yet another fight with those who say I shouldn't advocate database schema changes (yes, you know who you are :-), but here's how I'd do it.
1/ If you absolutely cannot change the schema, I would solve it with code (either real honest-to-goodness procedural code outside the database or as a stored procedure in whatever language your DBMS permits).
This would check the database for a non-yahoo name and return it, if there. If not there, it would attempt to return the yahoo name. If neither are there, it would return an empty data set.
2/ If you can change the schema and you want an SQL query to do the work, here's how I'd do it. Create a separate column in your table called CLASS which is expected to be set to 0 for non-yahoo addresses and 1 for yahoo addresses.
Create insert/update triggers to examine each addition or change of a row, setting the CLASS based on the email address (what it ends in). This guarantees that CLASS will always be set correctly.
When you query your table, order it by name and class, and only select the first row. This will give you the email address in the following preference: non-yahoo, yahoo, empty dataset.
Something like:
select name, email
from tbl
where name = '[name]'
order by name, class
fetch first row only;
If your DBMS doesn't have an equivalent to the DB2 "fetch first row only" clause, you'll probably still need to write code to only process one record.
If you want to process all names but only the specific desired email for that name, a program such as this will suffice (my views on trying to use a relational algebra such as SQL in a procedural way are pretty brutal, so I won't inflict them on you here):
# Get entire table contents sorted in name/class order.
resultSet = execQuery "select name, email from tbl order by name, class"
# Ensure different on first row
lastName = resultSet.value["name"] + "X"
# Process every single row returned.
while not resultSet.endOfFile:
# Only process the first in each name group (lower classes are ignored).
if resultSet.value["name"] != lastName:
processRow resultSet.value["name"] resultSet.value["email"]
# Store the last name so we can detect next name group.
lastName = resultSet.value["name"]
select ne.*
from name_email ne
where ne.email not like '%#yahoo.com' escape '\' or
not exists(
select 1 from name_email
where name = ne.name and
email not like '%#yahoo.com' escape '\'
)
You could use something like the following to exclude invalid email addresses:
SELECT name, email
FROM name_email
WHERE email NOT LIKE '%#yahoo.com' // % symbol is a wildcard so joe#yahoo.com and guy#yahoo.com both match this query.
AND name = 'Joe Guy';
Or do it like this to include only the valid email address or domain:
SELECT name, email
FROM name_email
WHERE email LIKE '%#gmail.com'
AND name = 'Joe Guy';
This works well if you know ahead of time what specific names you are querying for and what email addresses or domains you want to exclude or include.
Or if you don't care which email address you return but only want to return one, you could use something like this:
SELECT DISTINCT (name, email)
FROM name_email;
You could do
SELECT TOP 1 email
FROM name_email
WHERE name = 'Joe Guy'
ORDER BY case when email like '%yahoo.com' then 1 else 0 end
So sort them by *#yahoo.com last and anything else first, and take the first one.
EDIT: sorry, misread the question - you want a list of each name, with only one email, and a preference for non-yahoo emails. Probably can use the above along with a group by, I'll have to rethink it.
Grabbing all the rows from the database, knowing not what the names are (and not needing to care about that really), but just want them to show, and if matching, skip a match if the email contains, in this case, #yahoo.com
SELECT DISTINCT name, email FROM name_email
WHERE email NOT LIKE '%#yahoo.com'
GROUP BY name;
Doing that will grab all the rows, but only one of a record if the names match with another row. But then, if there are two rows with matching names, junk the one with #yahoo.com in the email.
Not very pretty, but I believe it should work
select
ne.name
,ne.email
from
name_email ne
inner join (
select
name
,count(*) as emails_per_name
from
name_email
group by name
) nec
on ne.name = nec.name
where
nec.emails_per_name = 1
or (nec.emails_per_name > 1 and ne.email not like ('%#yahoo.com'))
That is assuming that the duplicate emails would be in yahoo.com domain - as specified in your question, and those would be excluded if there is more than one email per name
If you are working with SQL Server 2005 or Oracle, you can easily solve your problem with a ranking (analytical) function.
select a.name, a.name_email
from (select name, name_email,
row_number() over (partition by name
order by case
when name_email like '%#yahoo.com' then 1
when name_email like '%#gmail.com' then 1
when ... (other 'generic' email) then 1
else 0
end) as rn) as a
where a.rn = 1
By assigning different values to the various generic email names you can even have 'preferences'. As it is written here, if you have both a yahoo and a gmail address, you can't predict which one will be picked up.
You could use a UNION for this. Select everything without the yahoo.com and then just select the records that have yahoo.com and is not in the first list.
SELECT DISTINCT (name, name_email) FROM TABLE
WHERE name_email NOT '%yahoo.com'
UNION
SELECT DISTINCT (name, name_email) FROM TABLE
WHERE name NOT IN (SELECT DISTINCT (name, name_email) FROM TABLE
WHERE name_email NOT '%yahoo.com')