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

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

Related

How to update a field that is a drop down list from another table using a SQL query

Hey guys in my access database I have two tables labeled 'Contacts' and 'StatusList'. StatusList consists of one column and four rows labeled:
StatusDescriptionSuspectProspectInquiryApplicant
Contacts consists of the following:
First Last Email Status Phone
The Status field in Contacts is a drop down box that feeds of off the StatusList table. Every row in the Contacts table is set to Suspect initially.
The email field in Contacts is set to only allow unique emails.
I have a table labeled ExcelImport that has the same fields as the Contacts table.
Eventually, I want to insert the the data from my ExcelImport table to Contacts. If there is an email match between ExcelImport and Contacts, I want the row in Contacts for the Status field to be updated from Suspect to Prospect.
So far I have this:
SELECT Contacts.contactEmail
FROM Contacts
Inner Join ExcelImport on Contacts.contactEmail = ExcelImport.contactEmail;
I know this only shows me the emails that match. Would there be any way to update the Status list for that specific row that matched to Prospect ?
As mentioned, simply change the value with an UPDATE action query and the dropdown will change accordingly. The dropdown if I understand you is simply a lookup combox box that you possibly set in the Table Design. It simply helps control the values entered. The UPDATE query will change that dropdown as value of table changes:
UPDATE Contacts INNER JOIN ExcelImport
ON Contacts.contactEmail = ExcelImport.contactEmail
SET Contacts.[Status] = 'Prospect';
Additionally, you mention I want to insert the the data from my ExcelImport table to Contacts which you can do so preserving the uniqueness of the Email field by running a NOT EXISTS
clause in append query.
INSERT INTO Contacts ([First], [Last], [Email]], [Status], [Phone])
SELECT e.[First], e.[Last], e.[Email]], e.[Status], e.[Phone]
FROM ExcelImport e
WHERE NOT EXISTS
(SELECT 1 FROM Contacts c WHERE c.[Email] = e.[Email])
Therefore, run both action queries to import unique Email and for repeated Email, update Status.

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

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!

What is the correct SQL statement to get the Row Source for a table, based on another field?

I currently have a schema set up in the following manner:
The table tblCategoryRiskArea is set up as an intermediate table for the many-to-many relationship that can exist between Categories and RiskAreas.
Within the tblBase table, I would like to make it so that the RiskArea choices are dependant upon the Category choice. MS Access allows you to set a Lookup for a field in a table based upon a Row Source SQL statement. I am having trouble figuring out the correct SQL statement to define the Row Source for RiskArea dependant upon Category. This:
SELECT tblRiskAreas.RiskAreaID, tblRiskAreas.RiskArea
FROM tblRiskAreas INNER JOIN
((tblCategories INNER JOIN tblBase
ON tblCategories.CategoryId = tblBase.Category)
INNER JOIN tblCategoryRiskArea
ON tblCategories.CategoryId = tblCategoryRiskArea.Category)
ON (tblRiskAreas.RiskAreaID = tblCategoryRiskArea.RiskArea)
AND (tblRiskAreas.RiskAreaID = tblBase.RiskArea)
WHERE (((tblCategoryRiskArea.Category)=[tblBase]![Category]))
ORDER BY tblRiskAreas.RiskAreaID;
is the best I've come up with so far, using MS Access' Query Builder, so all of the Inner Joins have been created just by my having defined the relationships between the tables and dragging them into the Query Builder. This query returns nothing, however.
I suspect that it may have something to do with the circular nature of the relationships I set up?
Thank you.
Edited: tblRiskArea contains 4 RiskAreas, as follows:
Environmental
Health
Safety
Security
Each Category can fall into one or two of these RiskAreas, so the tblCategoryRiskArea creates the relationship bewtween them.
First remove Category and RiskArea from tblBase and replace them for CategoriRiskAreaID
You will show in your form 2 combos. First combo Catagory data source:
Select CategoryId,Category from tblCategories
Second combo Risk Areas data source:
Select a.CategoryRiskId, b.RiskArea
from tblCategoryRiskArea a
inner join tblRiskArea b
where a.RiskAreaId=b.RiskAreaId
AND a.category = #ComboBoxCategorySelectedItem
Now you have the value to insert in tblBase, ComboBoxSelectedItem is tblCategoryRiskArea.CategoryRiskId

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)