LEFT JOIN on static list of items? - sql

DBMS is intersystems-cache!
Motivation: I need to do a left join on a table so I can get the same list of message types every time, even if the result is zero or null. Unfortunately, this is a large table so including a SELECT DISTINCT() is prohibitively slow. These should never change, so I thought I'd get the list once and just join them statically.
Based on another SO question, here is what I have to replace the SELECT DISTINCT():
SELECT 'HS.MESSAGE.GATEWAYREGISTRATIONREQUEST' as MessageBodyClassName
UNION SELECT 'HS.MESSAGE.MERGEPATIENTREQUEST'
UNION SELECT 'HS.MESSAGE.PATIENTSEARCHREQUEST'
This returns results that look exactly as expected, identical to the Distinct query. However, when I plug this into my JOIN statement, all the counts come back as zero.
Failing Query
SELECT mh.MessageBodyClassName, count(l.MessageBodyClassName) as MessageCount FROM
(
SELECT 'HS.MESSAGE.GATEWAYREGISTRATIONREQUEST' as MessageBodyClassName
UNION SELECT 'HS.MESSAGE.MERGEPATIENTREQUEST'
UNION SELECT 'HS.MESSAGE.PATIENTSEARCHREQUEST'
) mh LEFT JOIN
(
SELECT messageBodyClassName FROM ens.messageheader WHERE TimeCreated > DATEADD(hh, -1, GETUTCDATE())
) l ON mh.MessageBodyClassName = l.MessageBodyClassName
GROUP BY mh.MessageBodyClassName
Failed results
MessageBodyClassName MessageCount
------------------------------------- ------------
HS.MESSAGE.GATEWAYREGISTRATIONREQUEST 0
HS.MESSAGE.MERGEPATIENTREQUEST 0
HS.MESSAGE.PATIENTSEARCHREQUEST 0
Working Query
SELECT mh.MessageBodyClassName, count(l.MessageBodyClassName) as MessageCount FROM
(
SELECT DISTINCT(MessageBodyClassName) FROM ens.messageheader
) mh LEFT JOIN
(
SELECT messageBodyClassName FROM ens.messageheader WHERE TimeCreated > DATEADD(hh, -1, GETUTCDATE())
) l ON mh.MessageBodyClassName = l.MessageBodyClassName
GROUP BY mh.MessageBodyClassName
Working and expected results
MessageBodyClassName MessageCount
------------------------------------- ------------
HS.MESSAGE.GATEWAYREGISTRATIONREQUEST 0
HS.MESSAGE.MERGEPATIENTREQUEST 0
HS.MESSAGE.PATIENTSEARCHREQUEST 54
For VKP: Why are the results different? How can I adjust the first query with literals to get the proper (same) results?

The last thing I can think of is to run your DISTINCT query once into a permanent table in your database. That way the inner SELECT in your query will only have to process those three lines. The inner query would lose DISTINCT, like
SELECT MessageBodyClassName FROM ens.messageheader_permvals
EDIT: The below answer did not work
This may be a longshot, but if it doesn't work it might help you diagnose the problem. Instead of the UNION try
SELECT MessageBodyClassName FROM ens.messageheader
WHERE MessageBodyClassName in (
'HS.MESSAGE.GATEWAYREGISTRATIONREQUEST',
'HS.MESSAGE.MERGEPATIENTREQUEST',
'HS.MESSAGE.PATIENTSEARCHREQUEST')
That should return records only if those values actually exist in the table and are compatible with the format of MessageBodyClassName, which we know works using the DISTINCT version. I don't know if the performance will be better this way, but hopefully it will shed some light on the issue.
EDIT: the below answer does not apply, as the OP is was actually trying to select the literal quoted values
You don't have a FROM statements in your UNION query. Try
SELECT 'HS.MESSAGE.GATEWAYREGISTRATIONREQUEST' as MessageBodyClassName
FROM ens.messageheader
UNION SELECT 'HS.MESSAGE.MERGEPATIENTREQUEST'
FROM ens.messageheader
UNION SELECT 'HS.MESSAGE.PATIENTSEARCHREQUEST'
FROM ens.messageheader
The rest of the query looks right.

I agree with xQbert, problem is the hard codes values
Try
SELECT T1.MessageBodyClassName, T2.MessageBodyClassName
FROM (
SELECT 'HS.MESSAGE.GATEWAYREGISTRATIONREQUEST' as MessageBodyClassName
UNION SELECT 'HS.MESSAGE.MERGEPATIENTREQUEST'
UNION SELECT 'HS.MESSAGE.PATIENTSEARCHREQUEST'
) as T1
LEFT JOIN (
SELECT DISTINCT(MessageBodyClassName) as MessageBodyClassName
FROM ens.messageheader
) as T2
ON T1.MessageBodyClassName = T2.MessageBodyClassName
Possible solution: Create a temporal table
CREATE TABLE className as
SELECT DISTINCT(MessageBodyClassName) as MessageBodyClassName
FROM ens.messageheader

Related

SQL if statement to select items form different tables

I am creating a new table joining 3 different tables. The problem is that I have some data that I want to select for other_info divided into two different tables. table_1 has preference over table_2, but it is possible that in table_1 are missing values. So, I want to select the value of box if it's not empty from table_1 and select it from table_2 if the value in table_1 does not exist.
This is the code I have very simplified, but I think it's enough to see what I want to do. I've written an IF ... ELSE statement inside a with, and this is the error I get:
Syntax error: Expected "(" or keyword SELECT or keyword WITH but got keyword IF at [26:5]
Besides, I've tried different things inside the conditional of the if, but none of them is what I expect. Here is the code:
CREATE OR REPLACE TABLE `new_table`
PARTITION BY
Current_date
AS (
WITH info AS (
SELECT
Date AS Day,
Box,
FROM
`table_1`
),
other_info AS (
IF (...)
BEGIN{
SELECT
Date AS Day,
Box
FROM
`table_1`}
END
ELSE
BEGIN{
SELECT
Date AS Day,
Box
FROM
`table_2`}
END
)
SELECT
Date
Box
Box_description
FROM
`table_3`
LEFT JOIN info(Day)
LEFT JOIN other_info(Day)
)
You're not going to be able to embed an IF within a CTE or a Create-Table-As.
An alternative structure can be to union two queries with mutually exclusive WHERE clauses... (Such that only one of the two queries ever returns anything.)
For example, if the code below, something is checked for being NULL or NOT NULL, and so only one of the two can ever return data.
WITH
info AS
(
SELECT
Date AS Day,
Box,
FROM
`table_1`
),
other_info AS
(
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
THIS BIT
--------------------------------------------------------------------------------
SELECT
Date AS Day,
Box
FROM
`table_1`
WHERE
(SELECT MAX(x) FROM y) IS NULL
UNION ALL
SELECT
Date AS Day,
Box
FROM
`table_2`
WHERE
(SELECT MAX(x) FROM y) IS NOT NULL
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
)
SELECT
Date
Box
Box_description
FROM
`table_3`
LEFT JOIN info(Day)
LEFT JOIN other_info(Day)
In stead of the if..., you could do something like this (in MySQL):
SELECT *
FROM table1
UNION ALL
SELECT *
FROM table2 WHERE `date` NOT IN (SELECT `date` FROM table1)
I am not sure (as in: I did not test), but I do think this is also possible in google-bigquery
see: DBFIDDLE

use multiple LEFT JOINs from multiple datasets SQL

I need to perform multiple JOINs, I am grabbing the data from multiple tables and JOINing on id. The tricky part is that one table I need to join twice. Here is the code:
(
SELECT
content.brand_identifier AS brand_name,
CAST(timestamp(furniture.date) AS DATE) AS order_date,
total_hearst_commission
FROM
`furniture_table` AS furniture
LEFT JOIN `content_table` AS content ON furniture.site_content_id = content.site_content_id
WHERE
(
timestamp(furniture.date) >= TIMESTAMP('2020-06-01 00:00:00')
)
)
UNION
(
SELECT
flowers.a_merchant_name AS merchant_name
FROM
`flowers_table` AS flowers
LEFT JOIN `content` AS content ON flowers.site_content_id = content.site_content_id
)
GROUP BY
1,
2,
3,
4
ORDER BY
4 DESC
LIMIT
500
I thought I could use UNION but it gives me an error Syntax error: Expected keyword ALL or keyword DISTINCT but got "("
I'm not able to comment, but like GHB states, the queries do not have the same number of columns; therefore, UNION will not work here.
I think it would be helpful to know why sub-queries are needed in the first place. I'm guessing this query does not product the results you want, so please elaborate on why that is.
select
f.a_merchant_name as merchant_name,
c.brand_identifier as brand_name,
CAST(timestamp(f.date) AS DATE) AS order_date,
total_hearst_commission
from furniture_table f
left join content_table c on c.site_content_id = f.site_content_id
where timestamp(f.date) >= TIMESTAMP('2020-06-01 00:00:00')
group by 1,2,3,4

Oracle Left Join not returning all rows

I am using the following CTE. The first part collects all unique people and the second left joins the unique people with events during a particular time frame. I am expecting that all the rows be returned from my unique people table even if they don't have an event within the time frame. But this doesn't appear to be the case.
WITH DISTINCT_ATTENDING(ATTENDING) AS
(
SELECT DISTINCT ATTENDING
FROM PEOPLE
WHERE ATTENDING IS NOT NULL
), -- returns 62 records
EVENT_HISTORY(ATTENDING, TOTAL) AS
(
SELECT C.ATTENDING,
COUNT(C.ID)
FROM DISTINCT_ATTENDING D
LEFT JOIN PEOPLE C
ON C.ATTENDING = D.ATTENDING
AND TO_DATE(C.DATE, 'YYYYMMDD') < TO_DATE('20140101', 'YYYYMMDD')
GROUP BY C.ATTENDING
ORDER BY C.ATTENDING
)
SELECT * FROM EVENT_HISTORY; -- returns 49 rows
What am I doing wrong here?
Jonny
The problem is inthe column "C.ATTENDING", just change for "D.ATTENDING"
SELECT D.ATTENDING,
COUNT(C.ID)
FROM DISTINCT_ATTENDING D
LEFT JOIN PEOPLE C
ON C.ATTENDING = D.ATTENDING
AND TO_DATE(C.DATE, 'YYYYMMDD') < TO_DATE('20140101', 'YYYYMMDD')
GROUP BY D.ATTENDING
ORDER BY D.ATTENDING
Your query seems too complicated. I think the following does the same thing:
SELECT P.ATTENDING,
SUM(CASE WHEN TO_DATE(P.DATE, 'YYYYMMDD') < TO_DATE('20140101', 'YYYYMMDD')
THEN 1 ELSE 0 END)
FROM PEOPLE P
WHERE P.ATTENDING IS NOT NLL
GROUP BY P.ATTENDING
ORDER BY P.ATTENDING ;
Your problem is that you are aggregating by a column in the second table of a left join. This is NULL when there is no match.

SQL 2008 recursive query for message threads given an entry point of one mesage

Maybe an over elaborate title. Basically think of an email inbox. I have a table
as so:
As you can see, it is a recursive table, very simple, just has the parentID of a message, and as you can see with the green highlight ring, the end of the "chain" is when there is a NULL for the parentID.
What I need is to provide (for example) the INBOXID of 12, and return back all parents.. in this example I should get 1 record back which is INBOXID of 11.
In the second example, I should be able to pass in INBOXID of 9, but this time I should get back rows INBOXID 8,7 and 1
I did have some success with the following query:
with q as
(
select inboxid, parentid
from bizzbox
union all
select a.inboxid, a.parentid
from bizzbox a
inner join q on q.inboxID = a.parentID
)
select distinct * from q
.. but of course it returns all of the parents for any of the rows.. I know it is probably something really stupidly simple like a where clause on one of the selects.. but having tried it (i.e. to parameterize the passing in of the start point inboxid), I can't quite see what I need to do???
Any help much appreciated!!!!!
David.
Try with this:
WITH cte
AS ( SELECT InboxID,
ParentID
FROM BIZZBOX
WHERE InboxID = #inboxID
UNION ALL
SELECT prev.InboxID,
prev.ParentID
FROM BIZZBOX prev
INNER JOIN cte curr ON prev.InboxID = curr.ParentID ),
cte1
AS ( SELECT InboxID,
ParentID
FROM BIZZBOX
WHERE InboxID = #inboxID
UNION ALL
SELECT prev.InboxID,
prev.ParentID
FROM BIZZBOX prev
INNER JOIN cte1 curr ON prev.ParentID = curr.InboxID )
SELECT * FROM cte
UNION
SELECT * FROM cte1

MySQL intersection in subquery

I'm trying to create a filter for a list (of apartments), with a many-to-many relationship with apartment features through the apartsments_features table.
I would like to include only apartments that have all of some features (marked 'Yes' on a form) excluding all the ones that have any of another set features (marked 'No'). I realized too late that I couldn't use INTERSECT or MINUS in MySQL.
I have a query that looks something like:
SELECT `apartments`.* FROM `apartments` WHERE `apartments`.`id` IN (
SELECT `apartments`.`id` FROM `apartments` INTERSECT (
SELECT `apartment_id` FROM `apartments_features` WHERE `feature_id` = 103
INTERSECT SELECT `apartment_id` FROM `apartments_features` WHERE `feature_id` = 106
) MINUS (
SELECT `apartment_id` FROM `apartments_features` WHERE `feature_id` = 105 UNION
SELECT `apartment_id` FROM `apartments_features` WHERE `feature_id` = 107)
)
ORDER BY `apartments`.`name` ASC
I'm pretty sure there's a way to do this, but at the moment my knowledge is restricted to little more than simple left and right joins.
A slightly different way of doing it:
select a.*
from apartments a
join apartments_features f1
on a.apartment_id = f1.apartment_id and f1.feature_id in (103,106) -- applicable features
where not exists
(select null from apartments_features f2
where a.apartment_id = f2.apartment_id and f2.feature_id in (105,107) ) -- excluded features
group by f1.apartment_id
having count(*) = 2 -- number of applicable features
You could try something like this:
SELECT apartment_id
FROM
(
SELECT apartment_id
FROM apartments_features
WHERE feature_id IN (103, 106)
GROUP BY apartment_id
HAVING COUNT(*) = 2
) T1
LEFT JOIN
(
SELECT apartment_id
FROM apartments_features
WHERE feature_id IN (105, 107)
) T2
ON T1.apartment_id = T2.apartment_id
WHERE T2.apartment_id IS NULL
Join the result of this query to the apartments table to get the name, etc.