I'm having some difficulty trying to figure how to adjust my query. I'm not very good at SQL queries as it's not my forte. Anyway, I'm not sure what I'm doing wrong. Here's my table setup.
ID | Customer
---+-------------
1 | John
2 | Jane
3 | Steve
ID | Assets
---+-------------
1 | RealEstate
2 | Currency
3 | Stocks
CustomerID | AssetConfigurationId | Status
-----------+----------------------+-------
1 | 1 | E
1 | 2 | F
1 | 3 | X
2 | 3 | X
And if I query customer = 3, I want to get the following
AssetConfigurationId | Status
---------------------+------------
1 | null
2 | null
3 | X
Currently have this. I'm trying to understand how I can use left join to show all the assets and just have the values of the statuses to null for a specific customer. Right now it only shows the 3rd row. Trying to do this in a SQL Server stored procedure so that my .net application can get a list of the assets already and I'll just modify the statuses when it comes to converting them to objects.
select
ac.Id,
r.Status
from
assets ac
left join
assets_ref r on r.AssetConfigurationId = ac.Id
where
r.CustomerID = 3
Move your WHERE condition in the inner query.
select
ac.Id,
r.Status
from assets ac
left join
(select * from assets_ref where CustomerID = 3) r
on r.AssetConfigurationId = ac.Id;
You can use multiple conditions in JOINs:
select
ac.Id,
r.Status
from assets ac
left join assets_ref r
on r.AssetConfigurationId = ac.Id
and CustomerID = 3;
Related
I have two queries that I'm trying to combine into one result set.
Query 1:
SELECT t1.evalID, t2.[Order], COUNT(t2.StepID) AS 'Total Categories'
FROM Evals t1
JOIN Steps t2 ON t1.TemplateID = t2.TemplateID
JOIN GradingCats t3 ON t2.StepID = t3.StepID
GROUP BY t1.EvalID, t2.[Order]
ORDER BY t2.[Order]
Query 2:
SELECT t4.EvaluatorID, t6.StepID, t6.[Order], COUNT(t4.Grade) AS 'Grades Entered'
FROM Grading t4
JOIN GradingCats t5 ON t4.GradingCatID = t5.GradingCatID
JOIN Steps t6 ON t5.StepID = t6.StepID
GROUP BY t6.StepID, t4.EvaluatorID, t6.[Order]
My end goal is to locate which steps of an evaluation have missing grades.
edit (sample data):
Query #1
|---------------------|------------------|---------------------|
| evalID | Order | Total Categories |
|---------------------|------------------|---------------------|
| 81 | 01.00 | 17 |
|---------------------|------------------|---------------------|
| 81 | 02.00 | 17 |
|---------------------|------------------|---------------------|
| 81 | 03.00 | 17 |
|---------------------|------------------|---------------------|
Query #2
|---------------------|------------------|---------------------|------------------|
| evaluatorID | Step | Order | Grades Entered |
|---------------------|------------------|---------------------|------------------|
| 1178 | 609 | 01.00 | 2 |
|---------------------|------------------|---------------------|------------------|
| 1178 | 615 | 02.00 | 3 |
|---------------------|------------------|---------------------|------------------|
| 9441 | 609 | 01.00 | 17 |
|---------------------|------------------|---------------------|------------------|
| 9441 | 609 | 02.00 | 17 |
|---------------------|------------------|---------------------|------------------|
| 9441 | 609 | 03.00 | 17 |
|---------------------|------------------|---------------------|------------------|
Starting with the first query which shows all the steps associated with an EVAL, you can LEFT OUTER JOIN the second query, and the steps that are NULL on the right side of the query will be the ones that are missing grades.
In order to do this, there must be some way in your tables to link Grading to Evals. This column is not evident from the code you posted, but I will assume it is there. Maybe it's through GradingCats.
In shortened psuedo-code, just to show what I mean:
SELECT ...
FROM Evals e
INNER JOIN Steps s ON e.TemplateID = s.TemplateID
LEFT OUTER JOIN Grading g ON g.EvalID = e.EvalID --use whatever means you have to show which Eval a Grade is from
LEFT OUTER JOIN Steps gs ON {join to Grading through GradingCats as in your second query}
WHERE gs.StepID IS NULL
In analyzing the result of this query, all the Steps of every Eval will be in s.StepID, and when the same row has a NULL for gs.StepID, that means that step did not get a grade.
Note that you won't want to do any GROUP BY in this query, since you want a row-level analysis.
A coworker (with more knowledge of the data than I) slightly modified my query:
SELECT query1.stepID, Categories, Graded
FROM
(
SELECT rs.stepid, COUNT(c.category) AS 'Categories'
FROM Evals e
JOIN RunScriptSteps rs ON e.TemplateID = rs.TemplateID
JOIN GradingCats c ON rs.StepID = c.StepID
WHERE EvalID = *(someNumber)*
GROUP BY rs.stepid
)AS query1
LEFT JOIN
(
SELECT s.StepID, COUNT(Grade) AS 'Graded'
FROM Grading g
JOIN GradingCats c ON g.GradingCatID = c.GradingCatID
JOIN Steps s ON c.StepID = s.StepID
WHERE EvalID = *(someNumber)*
GROUP BY s.stepid
) AS query2
ON query1.stepid = query2.stepid
ORDER BY stepid ASC
I feel like I was always taught to use LEFT JOINs and I often see them mixed with INNERs to accomplish the same type of query throughout several pieces of code that are supposed to do the same thing on different pages. Here goes:
SELECT ac.reac, pt.pt_name, soc.soc_name, pt.pt_soc_code
FROM
AECounts ac
INNER JOIN 1_low_level_term llt on ac.reac = llt.llt_name
LEFT JOIN 1_pref_term pt ON llt.pt_code = pt.pt_code
LEFT JOIN 1_soc_term soc ON pt.pt_soc_code = soc.soc_code
LIMIT 100,10000
Thats one I am working on:
I see a lot like:
SELECT COUNT(DISTINCT p.`case`) as count
FROM FDA_CaseReports cr
INNER JOIN ae_indi i ON i.isr = cr.isr
LEFT JOIN ae_case_profile p ON cr.isr = p.isr
This seems like the LEFT may as well be INNER is there any catch?
Is there any catch? Yes there is -- left joins are a form of outer join, while inner joins are a form of, well, inner join.
Here's examples that show the difference. We'll start with the base data:
mysql> select * from j1;
+----+------------+
| id | thing |
+----+------------+
| 1 | hi |
| 2 | hello |
| 3 | guten tag |
| 4 | ciao |
| 5 | buongiorno |
+----+------------+
mysql> select * from j2;
+----+-----------+
| id | thing |
+----+-----------+
| 1 | bye |
| 3 | tschau |
| 4 | au revoir |
| 6 | so long |
| 7 | tschuessi |
+----+-----------+
And here we'll see the difference between an inner join and a left join:
mysql> select * from j1 inner join j2 on j1.id = j2.id;
+----+-----------+----+-----------+
| id | thing | id | thing |
+----+-----------+----+-----------+
| 1 | hi | 1 | bye |
| 3 | guten tag | 3 | tschau |
| 4 | ciao | 4 | au revoir |
+----+-----------+----+-----------+
Hmm, 3 rows.
mysql> select * from j1 left join j2 on j1.id = j2.id;
+----+------------+------+-----------+
| id | thing | id | thing |
+----+------------+------+-----------+
| 1 | hi | 1 | bye |
| 2 | hello | NULL | NULL |
| 3 | guten tag | 3 | tschau |
| 4 | ciao | 4 | au revoir |
| 5 | buongiorno | NULL | NULL |
+----+------------+------+-----------+
Wow, 5 rows! What happened?
Outer joins such as left join preserve rows that don't match -- so rows with id 2 and 5 are preserved by the left join query. The remaining columns are filled in with NULL.
In other words, left and inner joins are not interchangeable.
Here's a rough answer, that is sort of how I think about joins. Hoping this will be more helpful than a very precise answer due to the aforementioned math issues... ;-)
Inner joins narrow down the set of rows returns. Outer joins (left or right) don't change number of rows returned, but just "pick up" additional columns if possible.
In your first example, the result will be rows from AECounts that match the conditions specified to the 1_low_level_term table. Then for those rows, it tries to join to 1_pref_term and 1_soc_term. But if there's no match, the rows remain and the joined in columns are null.
An INNER JOIN will only return the rows where there are matching values in both tables, whereas a LEFT JOIN will return ALL the rows from the LEFT table even if there is no matching row in the RIGHT table
A quick example
TableA
ID Value
1 TableA.Value1
2 TableA.Value2
3 TableA.Value3
TableB
ID Value
2 TableB.ValueB
3 TableB.ValueC
An INNER JOIN produces:
SELECT a.ID,a.Value,b.ID,b.Value
FROM TableA a INNER JOIN TableB b ON b.ID = a.ID
a.ID a.Value b.ID b.Value
2 TableA.Value2 2 TableB.ValueB
3 TableA.Value3 3 TableB.ValueC
A LEFT JOIN produces:
SELECT a.ID,a.Value,b.ID,b.Value
FROM TableA a LEFT JOIN TableB b ON b.ID = a.ID
a.ID a.Value b.ID b.Value
1 TableA.Value1 NULL NULL
2 TableA.Value2 2 TableB.ValueB
3 TableA.Value3 3 TableB.ValueC
As you can see, the LEFT JOIN includes the row from TableA where ID = 1 even though there's no matching row in TableB where ID = 1, whereas the INNER JOIN excludes the row specifically because there's no matching row in TableB
HTH
Use an inner join when you want only the results that appear in both tables that matches the Join condition.
Use a left join when you want all the results from Table A, but if Table B has data relevant to some of Table A's records, then you also want to use that data in the same query.
Use a full join when you want all the results from both Tables.
For newbies, because it helped me when I was one: an INNER JOIN is always a subset of a LEFT or RIGHT JOIN, and all of these are always subsets of a FULL JOIN. It helped me understand the basic idea.
I have following Product table and ProductTag tables -
ID | Product
--------------
1 | Product_A
2 | Product_B
3 | Product_C
TagID | ProductID
----------------------
1 | 2
1 | 3
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
Now I need a SQL query that return all products list which are having both Tag 1 and 2. Result should be as given below -
ProductID | Product
------------------------
2 | Product_B
3 | Product_C
Please suggest how can i write a MS SQL query for this.
SELECT p.ID, p.Product
FROM Product p
INNER JOIN ProductTag pt
ON p.ID = pt.ProductID
WHERE pt.TagID IN (1, 2) -- <== Tags you want to find
GROUP BY p.ID, o.Product
HAVING COUNT(*) = 2 -- <== tag count on WHERE clause
however, if TagID is not unique on every Product, you need to count only the distinct product.
HAVING COUNT(DISTINCT pt.TagID) = 2
More on: SQL of Relational Division
Question is similar to this one How to write a MySQL query that returns a temporary column containing flags for whether or not an item related to that row exists in another table
Except that I need to be more specific about which rows exists
I have two tables: 'competitions' and 'competition_entries'
Competitions:
ID | NAME | TYPE
--------------------------------
1 | Example | example type
2 | Another | example type
Competition Entries
ID | USERID | COMPETITIONID
---------------------------------
1 | 100 | 1
2 | 110 | 1
3 | 110 | 2
4 | 120 | 1
I want to select the competitions but add an additional column which specifies whether the user has entered the competition or not. This is my current SELECT statement
SELECT
c.[ID],
c.[NAME],
c.[TYPE],
(CASE
WHEN e.ID IS NOT NULL AND e.USERID = #userid THEN 1
ELSE 0
END
) AS 'ENTERED'
FROM competitions AS c
LEFT OUTER JOIN competition_entries AS e
ON e.COMPETITIONID = c.ID
My desired result set from setting the #userid parameter to 110 is this
ID | NAME | TYPE | ENTERED
-------------------------------------
1 | Example | example type | 1
2 | Another | example type | 1
But instead I get this
ID | NAME | TYPE | ENTERED
-------------------------------------
1 | Example | example type | 0
1 | Example | example type | 1
1 | Example | example type | 0
2 | Another | example type | 1
Because it's counting the entries for all user ids
Fixing your query
SELECT
c.[ID],
c.[NAME],
c.[TYPE],
MAX(CASE
WHEN e.ID IS NOT NULL AND e.USERID = #userid THEN 1
ELSE 0
END
) AS 'ENTERED'
FROM competitions AS c
LEFT OUTER JOIN competition_entries AS e ON e.COMPETITIONID = c.ID
GROUP BY
c.[ID],
c.[NAME],
c.[TYPE]
An alternative is to rewrite it using EXISTS which is pretty much the same but may be easier to understand.
BTW, using single quotes on the column name is deprecated. Use square brackets.
SELECT
c.[ID],
c.[NAME],
c.[TYPE],
CASE WHEN EXISTS (
SELECT *
FROM competition_entries AS e
WHERE e.COMPETITIONID = c.ID
AND e.USERID = #userid) THEN 1 ELSE 0 END [ENTERED]
FROM competitions AS c
I have a database with 5 tables that have related data..
it looks something like this..
The table "associate_payin_ad" stores the date of registration & annexure id. Physically an Annexure is just a piece of paper which can have zero or more "Payin" or "Associate" entries..
Also 'payin' & 'associate' tables have multiple mode's of payment (like cash, cheque, bdcash, bdcheque) for the [amount] & [payment] column.. there are separate tables present for bycash, bycheque, bybdcash & bybdcheque, I have shown just the 'bycash' tables...
If the tables are filled with the following below given data..
[associate_payin_ad] Table:
adid | date_register | annexure_id
1 | 05/12/2011 | 1
2 | 05/12/2011 | 2
3 | 06/12/2011 | 1
4 | 07/12/2011 | 1
[payin] Table:
fid | amount | adid
1 | 10000 | 1 [this entry was made on 05/12/2011 in annexure no 1]
2 | 10000 | 1 [this entry was made on 05/12/2011 in annexure no 1]
3 | 40000 | 2 [this entry was made on 05/12/2011 in annexure no 2]
4 | 10000 | 4 [this entry was made on 07/12/2011 in annexure no 1]
[payin_bycash] Table:
fid | bycash
1 | 10000
2 | 10000
3 | 40000
4 | 10000
[associate] table...
aid | payment | adid
1 | 200 | 1 [this entry was made on 05/12/2011 in annexure no 1]
2 | 200 | 3 [this entry was made on 06/12/2011 in annexure no 1]
[associate_bycash] table...
aid | bycashajf
1 | 200
2 | 200
I need the SUM of [payin_bycash.bycash] & [associate_bycash.bycashajf] for a particular date range.. (for eg. 05/12/2011 to 07/12/2011)
date_register | amount
05/12/2011 | 60200
06/12/2011 | 200
07/12/2011 | 10000
I have been running around in circles since yesterday trying to figure out the appropriate query.. the best I could come up with it is this, but in vain:
SELECT apad.date_register,
SUM(ISNULL(pica.cash_in_hand, 0)) + SUM(ISNULL(aca.bycashajf, 0)) AS amount
FROM associate_payin_ad AS apad LEFT OUTER JOIN
payin AS pi ON apad.adid = pi.adid INNER JOIN
payin_bycash AS pica ON pi.fid = pica.fid
LEFT OUTER JOIN associate AS asso ON apad.adid = asso.adid INNER JOIN
associate_bycash AS aca ON asso.aid = aca.aid
WHERE (apad.date_register BETWEEN #date_initial AND #date_final)
GROUP BY apad.date_register
The above query returns me just this..
date_register | amount
05/12/2011 | 20400
What am i doing wrong?
thnx in advance
You can't mix inner and outer joins like that. When you use a left outer join, it will return null records in the right hand table to ensure that all rows from the left hand table are returned as expected. However, if you then try to join the right hand table to another table using an INNER join, the null records will be filtered out as you won't have matching null records in the other table.
In your case, this is happening when you join to payin. You'll get a row for aid=3, but then that row is filtered out when you try to join to payin_bycash, as aid=3 doesn't exist in payin.. Same problem for your join to associate.
The best way to around this problem is to left join to a subquery (or you could do it with a CRE).. Try this:
SELECT apad.date_register,
SUM(ISNULL(pica.cash_in_hand, 0)) + SUM(ISNULL(aca.bycashajf, 0)) AS amount
FROM associate_payin_ad AS apad
LEFT OUTER JOIN
(
SELECT payin_bycash.cash_in_hand
FROM payin
INNER JOIN payin_bycash ON payin.fid = payin_bycash.fid
) pi ON apad.adid = pi.adid
LEFT OUTER JOIN
(
SELECT associate_bycash.bycashajf
FROM associate
INNER JOIN associate_bycash ON associate.aid = associate_bycash.aid
) asso ON apad.adid = asso.adid
WHERE (apad.date_register BETWEEN #date_initial AND #date_final)
GROUP BY apad.date_register
Also, have a read of this: http://weblogs.sqlteam.com/jeffs/archive/2007/10/11/mixing-inner-outer-joins-sql.aspx