Long time lurker, first time poster.
I have two tables 'case' and 'case_char'.
case
case_id | status | date
1 | closed | 01/01/2014
2 | open | 02/01/2014
case_char
case_id | property_key | value
1 | email | xx#xx.com
1 | phone | 1234567
2 | email | x2#xx.com
2 | phone | 987654
2 | issue | Unhappy
Say I want to return the 'issue' for each case. Not all cases have issues so I will need to do a left outer join. Unfortunately it is not working for me, it is returning only cases with the 'issue' characteristic. I need it to return all cases regardless of whether the 'issue' characteristic exists for a case in the case_char table.
Below is an example of the way I have written the code ( bearing in mind I am using an Oracle DB).
Could any of you whizzes help a brother out?
SELECT c.case_id, char.value
FROM case c, case_char char
WHERE c.case_id = char.case_id (+)
AND char.property_key = 'issue'
Just add a Join(+) to your property key as below:
SELECT C.CASE_ID, CHAR.VALUE
FROM CASE C, CASE_CHAR CHAR
WHERE
C.CASE_ID = CHAR.CASE_ID (+)
AND
CHAR.PROPERTY_KEY(+) = 'ISSUE';
^
|
You shoud use an explicit join, and put the property_key in the ON clause.
SELECT c.case_id, char.value
FROM case AS c
LEFT JOIN case_char AS char ON c.case_id = char.case_id AND char.property_key = 'issue'
I'm not very familiar with the syntax for implicit out joins. My guess is you need to put (+) after char.property_key = 'issue' to keep it from filtering out the null rows.
I assume you want 1 row per case regardless of whether or not it has an issue, but all the issues for each case?
If that's what you want, something close to this should work (I'm a SQL Server guy, so I'm not totally sure that this will work with Oracle).
SELECT
c.case_id
,char.value
FROM case AS c
LEFT JOIN case_char AS char
ON
c.case_id = char.case_id
AND char.property_key = 'issue'
Basically, we've moved the filter logic to the join condition, otherwise the WHERE clause will filter out anything that's not an 'issue'.
Does that answer your question?
Related
I have the following SQL query:
SELECT TOP 3 accounts.username
,COUNT(accounts.username) AS count
FROM relationships
JOIN accounts ON relationships.account = accounts.id
WHERE relationships.following = 4
AND relationships.account IN (
SELECT relationships.following
FROM relationships
WHERE relationships.account = 8
);
I want to return the total count of accounts.username and the first 3 accounts.username (in no particular order). Unfortunately accounts.username and COUNT(accounts.username) cannot coexist. The query works fine removing one of the them. I don't want to send the request twice with different select bodies. The count column could span to 1000+ so I would prefer to calculate it in SQL rather in code.
The current query returns the error Column 'accounts.username' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. which has not led me anywhere and this is different to other questions as I do not want to use the 'group by' clause. Is there a way to do this with FOR JSON AUTO?
The desired output could be:
+-------+----------+
| count | username |
+-------+----------+
| 1551 | simon1 |
| 1551 | simon2 |
| 1551 | simon3 |
+-------+----------+
or
+----------------------------------------------------------------+
| JSON_F52E2B61-18A1-11d1-B105-00805F49916B |
+----------------------------------------------------------------+
| [{"count": 1551, "usernames": ["simon1", "simon2", "simon3"]}] |
+----------------------------------------------------------------+
If you want to display the total count of rows that satisfy the filter conditions (and where username is not null) in an additional column in your resultset, then you could use window functions:
SELECT TOP 3
a.username,
COUNT(a.username) OVER() AS cnt
FROM relationships r
JOIN accounts a ON r.account = a.id
WHERE
r.following = 4
AND EXISTS (
SELECT 1 FROM relationships t1 WHERE r1.account = 8 AND r1.following = r.account
)
;
Side notes:
if username is not nullable, use COUNT(*) rather than COUNT(a.username): this is more efficient since it does not require the database to check every value for nullity
table aliases make the query easier to write, read and maintain
I usually prefer EXISTS over IN (but here this is mostly a matter of taste, as both techniques should work fine for your use case)
When LEFT JOINing two tables, is there a way to set the cells which can not be matched (NULL) to a custom value? So e.g. when the result returns, the NULL-cells actually HAVE a value, e.g. "N/A" or "Not found"?
I want to do this in MS Access 2003
Example:
| id | value | | id | other value |
|----|-------| LEFT JOIN |----|-------------|
| 1 | hello | -- id --> | 2 | world |
| 2 | you |
results in:
| id | value | other value |
| 1 | hello | NULL |
| 2 | you | world |
but should be:
| id | value | other value |
| 1 | hello | custom-val |
| 2 | you | world |
You can use Nz() to substitute an arbitrary value for a NULL;
SELECT Nz(F, "Not Present") FROM T
Would return either the value of field F, or "Not Present" if F were NULL.
Bear in mind that SQL’s outer join is a kind of relational union which is explicitly designed to project null values. You want to avoid using the null value (a good thing too, in my opinion), therefore you should avoid using outer joins. Note that modern relational languages have dispensed with the concept of null and outer join entirely (see endnote).
This outer join:
SELECT DISTINCT T1.id, T1.value, T2.other_value
FROM T1
LEFT OUTER JOIN T2
ON T1.id = T2.id;
…is semantically equivalent to this SQL code:
SELECT T1.id, T1.value, T2.other_value
FROM T1
INNER JOIN T2
ON T1.id = T2.id
UNION
SELECT T1.id, T1.value, NULL
FROM T1
WHERE NOT EXISTS (
SELECT *
FROM T2
WHERE T1.id = T2.id
);
The second query may look long winded but that’s only because of the way SQL has been designed/evolved. The above is merely a natural join, a union and a semijoin. However, SQL has no semijoin operator, requires you to specify column lists in the SELECT clause and to write JOIN clauses if your product hasn’t implemented Standard SQL’s NATURAL JOIN syntax (Access hasn’t), which results in a lot of code to express something quite simple.
Therefore, you could write code such as the second query above but using an actual default value rather than the null value.
The only relational game in town is the specification of a D language know as "The Third Manifesto" by Chris Date and Hugh Darwen. It explicitly rejects Codd's nulls (latterly Codd proposed two kinds of null) doesn't accommodate an outer join operator (in more recent writings the authors have proposed relation-valued attributes as an alternative to outer join). Specific citations:
C. J. Date (2009): SQL and Relational Theory: How to Write Accurate SQL Code: Ch 4, 'A remark on outer join' (p.84)
Darwen, Hugh (2003): The Importance of Column Names: "Note that in Tutorial D, the only 'join' operator is called JOIN, and it means 'natural join'." (p.16)
C. J. Date and Hugh Darwen (2006): Databases, Types and the Relational Model: The Third Manifesto: Proscription 4: "D shall include no concept of a 'relation' in which some 'tuple' includes some 'attribute' that does not have a value."
I have two tables in an Access database, tblProducts and tblProductGroups.
I am trying to run a query that joins both of these tables, and brings back a single record for each product. The problem is that the current design allows for a product to be listed in the tblProductGroups table more than 1 - i.e. a product can be a member of more than one group (i didnt design this!)
The query is this:
select tblProducts.intID, tblProducts.strTitle, tblProductGroups.intGroup
from tblProducts
inner join tblProductGroups on tblProducts.intID = tblProductGroups.intProduct
where tblProductGroups.intGroup = 56
and tblProducts.blnActive
order by tblProducts.intSort asc, tblProducts.curPrice asc
At the moment this returns results such as:
intID | strTitle | intGroup
1 | Product 1 | 1
1 | Product 1 | 2
2 | Product 2 | 1
2 | Product 2 | 2
Whereas I only want the join to be based on the first matching record, so that would return:
intID | strTitle | intGroup
1 | Product 1 | 1
2 | Product 2 | 1
Is this possible in Access?
Thanks in advance
Al
This option runs a subquery to find the minimum intGoup for each tblProducts.intID.
SELECT tblProducts.intID
, tblProducts.strTitle
, (SELECT TOP 1 intGroup
FROM tblProductGroups
WHERE intProduct=tblProducts.intID
ORDER BY intGroup ASC) AS intGroup
FROM tblProducts
WHERE tblProducts.blnActive
ORDER BY tblProducts.intSort ASC, tblProducts.curPrice ASC
This works for me. Maybe this helps someone:
SELECT
a.Lagerort_ID,
FIRST(a.Regal) AS frstRegal,
FIRST(a.Fachboden) AS frstFachboden,
FIRST(a.xOffset) AS frstxOffset,
FIRST(a.yOffset) AS frstyOffset,
FIRST(a.xSize) AS frstxSize,
FIRST(a.ySize) AS frstySize,
FIRST(a.Platzgr) AS frstyPlatzgr,
FIRST(b.Artikel_ID) AS frstArtikel_ID,
FIRST(b.Menge) AS frstMenge,
FIRST(c.Breite) AS frstBreite,
FIRST(c.Tiefe) AS frstTiefe,
FIRST(a.Fachboden_ID) AS frstFachboden_ID,
FIRST(b.BewegungsDatum) AS frstBewegungsDatum,
FIRST(b.ErzeugungsDatum) AS frstErzeugungsDatum
FROM ((Lagerort AS a)
LEFT JOIN LO_zu_ART AS b ON a.Lagerort_ID = b.Lagerort_ID)
LEFT JOIN Regal AS c ON a.Regal = c.Regal
GROUP BY a.Lagerort_ID
ORDER BY FIRST(a.Regal), FIRST(a.Fachboden), FIRST(a.xOffset), FIRST(a.yOffset);
I have non unique entries for Lagerort_ID on the table LO_zu_ART. My goal was to only use the first found entry from LO_zu_ART to match into Lagerort.
The trick is to use FIRST() an any column but the grouped one. This may also work with MIN() or MAX(), but I have not tested it.
Also make sure to call the Fields with the "AS" statement different than the original field. I used frstFIELDNAME. This is important, otherwise I got errors.
Create a new query, qryFirstGroupPerProduct:
SELECT intProduct, Min(intGroup) AS lowest_group
FROM tblProductGroups
GROUP BY intProduct;
Then JOIN qryFirstGroupPerProduct (instead of tblProductsGroups) to tblProducts.
Or you could do it as a subquery instead of a separate saved query, if you prefer.
It's not very optimal, but if you're bringing in a few thousand records this will work:
Create a query that gets the max of tblProducts.intID from one table and call it qry_Temp.
Create another query and join qry_temp to the table you are trying to join against, and you should get your results.
I've a database table, which has the following form
catID | category | parentID
1 | firstCategory | null
2 | SubCategory1 | 1
3 | SubCategory2 | 1
4 | subSub1 | 3
and so on...
There are multiple level of categories. What query can be used to get the records in the following format:
catID | category
1 | firstCategory
2 | firstCategory/SubCategory1
3 | firstCategory/SubCategory2
4 | firstCategory/SubCategory2/subSub1
The category id will be the id of the last category. How to write a query to join the categories to all levels? The exact number of levels for different categories is different?
I'm using mySQL.
For a maximum depth of 6 (including root), you can use this
select l0.catID,
concat(
case when l5.catID is null then '' else concat(l5.category, '/') end
, case when l4.catID is null then '' else concat(l4.category, '/') end
, case when l3.catID is null then '' else concat(l3.category, '/') end
, case when l2.catID is null then '' else concat(l2.category, '/') end
, case when l1.catID is null then '' else concat(l1.category, '/') end
, l0.category)
from catcat l0
left join catcat l1 on l0.parentID=l1.catID
left join catcat l2 on l1.parentID=l2.catID
left join catcat l3 on l2.parentID=l3.catID
left join catcat l4 on l3.parentID=l4.catID
left join catcat l5 on l4.parentID=l5.catID
Expand the pattern as required for longer max depths.
Oracle has this functionality, and the company I work for uses it for exactly what you are describing. The queries can be quite heavy at times though. A good writeup of the functions ("start with" and "connect by" keywords) is found here at this link, along with pseudo code you might try to wrap your head around...though cyberkiwi's answer is probably just fine for all practical purposes...
http://www.adp-gmbh.ch/ora/sql/connect_by.html
There is an alternative to what cyberkiwi said: Query the whole table and to the tree building in memory. Imperative languages are well suited for that while SQL is not. The performance will be much better (because SQL has to scan the table not only once but for every level).
I have a query I need to perform to show search results for a project. What needs to happen, I need to sort the results by the "horsesActiveDate" and this applies to all of them except for any ad with the adtypesID=7. Those results are sorted by date but they must always result after all other ads.
So I will have all my ads in the result set be ordered by the Active Date AND adtypesID != 7. After that, I need all adtypesID=7 to be sorted by Active Date and appended at the bottom of all the results.
I'm hoping to put this in one query instead of two and appending them together in PHP. The way the code is written, I have to find a way to get it all in one query.
So here is my original query which has worked great until I had to ad the adtypesID=7 which has different sorting requirements.
This is the query that exists now that doesn't take into account the adtypesID for sorting.
SELECT
horses.horsesID,
horsesDescription,
horsesActiveDate,
adtypesID,
states.statesName,
horses_images.himagesPath
FROM horses
LEFT JOIN states ON horses.statesID = states.statesID
LEFT JOIN horses_images ON horses_images.himagesDefault = 1 AND horses_images.horsesID = horses.horsesID AND horses_images.himagesPath != ''
WHERE
horses.horsesStud = 0
AND horses.horsesSold = 0
AND horses.horsesID IN
(
SELECT DISTINCT horses.horsesID
FROM horses
LEFT JOIN horses_featured ON horses_featured.horsesID = horses.horsesID
WHERE horses.horsesActive = 1
)
ORDER BY adtypesID, horses.horsesActiveDate DESC
My first thought was to do two queries where one looked for all the ads that did not contain adtypesID=7 and sort those as the query does, then run a second query to find only those ads with adtypesID=7 and sort those by date. Then take those two results and append them to each other. Since I need to get this all into one query, I can't use a php function to do that.
Is there a way to merge the two query results one after the other in mysql? Is there a better way to run this query that will accomplish this sorting?
The Ideal Results would be as below (I modified the column names so they would be shorter):
ID | Description | ActiveDate | adtypesID | statesName | himagesPath
___________________________________________________________________________
3 | Ad Text | 06-01-2010 | 3 | OK | image.jpg
2 | Ad Text | 05-31-2010 | 2 | LA | image1.jpg
9 | Ad Text | 03-01-2010 | 4 | OK | image3.jpg
6 | Ad Text | 06-01-2010 | 7 | OK | image5.jpg
6 | Ad Text | 05-01-2010 | 7 | OK | image5.jpg
6 | Ad Text | 04-01-2010 | 7 | OK | image5.jpg
Any help that can be provided will be greatly appreciated!
I am not sure about the exact syntax in MySQL, but something like
ORDER BY case when adtypesID = 7 then 2 else 1 end ASC, horses.horsesActiveDate DESC
would work in many other SQL dielects.
Note that most SQL dialects allow the order by to not only be a column, but an expression.
This should work:
ORDER BY (adtypesID = 7) ASC, horses.horsesActiveDate DESC
Use a Union to append two queries together, like this:
SELECT whatever FROM wherever ORDER BY something AND adtypesID!=7
UNION
SELECT another FROM somewhere ORDER BY whocares AND adtypesID=7
http://dev.mysql.com/doc/refman/5.0/en/union.html
I re-wrote your query as:
SELECT h.horsesID,
h.horsesDescription,
h.horsesActiveDate,
adtypesID,
s.statesName,
hi.himagesPath
FROM HORSES h
LEFT JOIN STATES s ON s.stateid = h.statesID
LEFT JOIN HORSES_IMAGES hi ON hi.horsesID = h.horsesID
AND hi.himagesDefault = 1
AND hi.himagesPath != ''
LEFT JOIN HORSES_FEATURED hf ON hf.horsesID = h.horsesID
WHERE h.horsesStud = 0
AND h.horsesSold = 0
AND h.horsesActive = 1
ORDER BY (adtypesID = 7) ASC, h.horsesActiveDate DESC
The IN subquery, using a LEFT JOIN and such, will mean that any horse record whose horsesActive value is 1 will be returned - regardless if they have an associated HORSES_FEATURED record. I leave it to you for checking your data to decide if it should really be an INNER JOIN. Likewise for the STATES table relationship...