How to avoid `missing expression` error in sql - sql

I'd like to count number of each product by following sql
but I suffered following error..
ORA-00936: Missing Expression
Where is wrong with my sql.
If someone has opinion,please let me know.
Thanks
select t.customer,
count(case when left(t.product,2)='AD' then 1 end) as A,
count(case when left(t.product,1)='F' then 1 end) as B,
count(case when left(t.product,1)='H' then 1 end) as C,
from table t
left join table2 t2
on t.customer = t2.customer
where t2.type='VLI'
group by t.customer

Oracle doesn't have LEFT() function while MySQL does, use SUBSTR() instead. And remove the comma, which's typo , at the end of the fourth line
SELECT t.customer,
COUNT(CASE
WHEN SUBSTR(t.product, 1, 2) = 'AD' THEN
1
END) AS A,
COUNT(CASE
WHEN SUBSTR(t.product, 1, 1) = 'F' THEN
1
END) AS B,
COUNT(CASE
WHEN SUBSTR(t.product, 1, 1) = 'H' THEN
1
END) AS C
FROM yourtable t
LEFT JOIN table2 t2
ON t.customer = t2.customer
WHERE t2.type = 'VLI'
GROUP BY t.customer

You have extra comma in row count(case when left(t.product,1)='H' then 1 end) as C,.
Delete the last comma and this error will go away.

I would suggest LIKE:
select t.customer,
sum(case when t.product like 'AD%' then 1 else 0 end) as A,
sum(case when t.product like 'F%' then 1 else 0 end) as B,
sum(case when t.product like 'H%' then 1 else 0 end) as C
from table t left join
table2 t2
on t.customer = t2.customer and t2.type='VLI'
group by t.customer;
Notes:
With like, you don't have to worry about an argument to a substring function matching the length of the matched string. I learned about the issues of inconsistencies there a long, long, long time ago.
I prefer sum() in this case over count(), but that is mostly aesthetic.
You are using a left join, but filtering in the where clause. This turns the join into an inner join. Either change the join to inner join or move the condition to the on clause (as this does).

Related

IN/EXISTS predicate sub-queries can only be used in Filter/Join and a few commands

I am trying to write a query in azure databricks and I am getting the following error
"IN/EXISTS predicate sub-queries can only be used in Filter/Join and a few commands"
This is the code I am using.
SELECT id,
(CASE WHEN id in (SELECT id from aTable) THEN 1 ELSE 0 END) as a,
(CASE WHEN id in (SELECT id from bTable) THEN 1 ELSE 0 END) as b,
(CASE WHEN id in (SELECT id from cTable) THEN 1 ELSE 0 END) as c
FROM table
I read that sql doesn't let you do this because the case statements are evaluated row by row, and it wants to prevent you from doing a SELECT statement for each row evaluation. If that is the case, is there an alternative or workaround to accomplish this? Thanks
Databricks does not support subqueries using IN or EXISTS in CASE statements. As an alternative, consider outer joining each view to master table:
Query could be like then structure below:
select .....
case when a.id is not null then a
when b.id is not null then b
end as id
from Table_t t LEFT JOIN (select id from aTable ) a ON t.id=a.id LEFT JOIN(
select id from bTable) b ON t.id=b.id
..................
I tried to reproduce similar scenario and got same error:
Regardless of whether it is contained in a CASE WHEN, the IN operator utilising a subquery only functions in filters, not projections. If you explicitly supply values in the IN clause as opposed to using a subquery, it works just great.
To work around this, I tried left join to tables and then check for a null in the case statement.
This query might work
%sql
SELECT t.Id,
(CASE WHEN at.Id is not null THEN 1 ELSE 0 END) as a,
(CASE WHEN bt.Id is not null THEN 1 ELSE 0 END) as b,
(CASE WHEN ct.Id is not null THEN 1 ELSE 0 END) as c
FROM table t
LEFT JOIN aTable at ON t.Id = at.Id
LEFT JOIN bTable bt ON t.Id = bt.Id
LEFT JOIN cTable ct ON t.Id = ct.Id
Sample data:
Output:

Left join two tables returning null values

I have these 2 tables #tmp and #tmp1:
I am writing a query like,
SELECT
repperiod, sequence, t1.title, question,
(CASE WHEN t2.Answer = 1 THEN 1 ELSE 0 END) Met,
(CASE WHEN t2.Answer = 3 THEN 1 ELSE 0 END) NA,
(CASE WHEN (ISNULL(t2.Answer,3) <> 3) THEN 1 ELSE 0 END) MetNotMet
FROM
#tmp t1
LEFT OUTER JOIN
#temp1 t2 ON t1.ScheduleQuestionID = t2.ScheduleQuestionID
AND t1.deptsplit = t2.DeptSplit
It's returning Met, NA, MetNotMet with NULL values.
What's wrong in the query?
#tmp has more rows than #tmp1. So not all Question Ids from #tmp are present in #tmp1.
Some records have multiple answers for same question. so I am splitting by dept.
I want to take all rows from #tmp. So I am creating left join.

Aggregating SQL results, two tables, then summarizing results

Using MSSQL
I have a table of users, and a table of products to which they are subscribed. Those subscriptions are either Free (F) or Paid (P). I have joined the tables, converted the F/P value to numeric using a case statement, and then summed up those values by user ID, the idea being that anyone with only free accounts will have a sum of 0, those with at least one paid account a value 1 or greater. I've gotten this far with the following:
SELECT t1.user_id, SUM(
CASE
WHEN t2.free_paid = 'P'
THEN 1
ELSE 0
END ) as highest
FROM users t1 INNER JOIN accounts t2
ON t1.user_id = t2.user_id
WHERE t2.account = 'A'
GROUP BY t1.user_id
ORDER BY t1.user_id
This yields a result like:
755 2
1259 2
2031 1
3888 0
Meaning that all but 3888 have at least one paid account.
But now I would like to simply add those up somehow to get our two values, one a count of users with at least one paid account (3 in example), and a count of those with only free accounts (1 in example).
I tried declare two variables, e.g. #free and #paid and using a case statement to add to those values by wrapping that around the above and treating it as a subquery, but I am unable to get that run.
Any ideas?
Re-using the query from the question you can create a CTE (or a subquery) and aggregate the results:
;WITH CTE_UserAccounts AS (
SELECT t1.user_id, SUM(
CASE
WHEN t2.free_paid = 'P'
THEN 1
ELSE 0
END ) as highest
FROM users t1 INNER JOIN accounts t2
ON t1.user_id = t2.user_id
WHERE t2.account = 'A'
GROUP BY t1.user_id
)
SELECT
COUNT(CASE WHEN highest > 0 THEN 1 END) AS [Paid],
COUNT(CASE WHEN highest = 0 THEN 1 END) AS [Free]
FROM
CTE_UserAccounts;
SELECT
COUNT(DISTINCT CASE WHEN t2.free_paid = 'P' THEN t1.user_id END) as atleast_one_paid,
COUNT(DISTINCT CASE WHEN t2.free_paid <> 'P' THEN t1.user_id END) as onlyfree
FROM users t1
INNER JOIN accounts t2 ON t1.user_id = t2.user_id
WHERE t2.account = 'A'
You could just wrap around your original query and sum it up
SELECT SUM (case when highest > 0 THEN 1 else 0 END) as UsersWithPaidAccount,
SUM (case when highest = 0 THEN 1 else 0 END) as UsersWithOnlyFreeAccount
FROM (SELECT t1.user_id, SUM(
CASE
WHEN t2.free_paid = 'P'
THEN 1
ELSE 0
END ) as highest
FROM users t1 INNER JOIN accounts t2
ON t1.user_id = t2.user_id
WHERE t2.account = 'A'
GROUP BY t1.user_id)
as DerivedTable

SQL JOIN WITH TWO WHERE CLAUSES

I would like to implement the following SQL query : suppose using JOIN clause, due to now it's running quite slow:
SELECT ID_USER, NICK
FROM TABLE1
WHERE ID_USER IN
(
SELECT ID_INDEX1
FROM TABLE2
WHERE ID_INDEX2 = '2'
)
AND ID_USER NOT IN
(
SELECT ID_INDEX2
FROM TABLE2
WHERE ID_INDEX1 = '2' AND GO ='NO'
)
ORDER BY NICK ASC
You could do the "including" part with INNER JOIN and the "excluding" part with a "LEFT JOIN" + filtering:
SELECT DISTINCT t1.ID_USER, t1.NICK
FROM TABLE1 t1
INNER JOIN TABLE2 t2IN
ON t1.ID_USER = t2IN.ID_INDEX1
AND t2IN.ID_INDEX2 = '2'
LEFT JOIN TABLE2 t2OUT
ON t1.ID_USER = t2OUT.ID_INDEX2
AND t2OUT.ID_INDEX1 = '2'
AND t2OUT.GO = 'NO'
WHERE t2OUT.ID_INDEX IS NULL
ORDER BY t1.NICK ASC
Assuming that you want to filter by ID_INDEX1 in both cases (see my comment on your question), you can:
count the number of rows per user in table2 with value = 2
count the number of rows per user in table2 with value = 2 and go = 'NO'
return only those for which the first count is greater than 0 and the second count equals 0
i.e.:
select * from (
select
id_user,
nick,
sum(case when table2.id_index2 = '2' then 1 else 0 end) as count2_overall,
sum(case when table2.id_index2 = '2' and go = 'NO' then 1 else 0 end) as count2_no
from table1
join table2 on table1.id_user = table2.id_index1
group by id_user, nick
)
where count2_overall > 0 and count2_no = 0

GROUP BY CLAUSE USE

I have a table with different payment method types: CHECK, MONEY ORDER, CASH. I need to show the break down of different payment types, following is my query. Could anyone suggest or comment how to do it optimally.
select
COUNT(T3.PAY_METHOD),
T1.CLAIM_ID
from TB1 T3
JOIN TB2 T2 ON T3.CLAIM_ID = T2.CLAIM_ID
GROUP BY T3.PAYMENT_METHOD_CD
Following is output column should look
|CHECK|MONEY ORDER|CASHIER'S CHECK|CASH|CREDIT CARD|
Displays the total count of all payments received via CHECK that were applied to the specific LIABLE INDIVIDUAL.
If you really need the output as columns, then you will want to use a SUM(case when method = '' then 0 else 1 end) type solution. Like this.
select t1.claim_id,
sum(case when t3.pay_method = 'CASH' then 1 else 0 end) as "CASH",
sum(case when t3.pay_method = 'MONEY ORDER' then 1 else 0 end) as "MONEY ORDER",
sum(case when t3.pay_method = 'CHECK' then 1 else 0 end) as "CHECK",
T1.CLAIM_ID
from TB1 T3
JOIN TB2 T2 ON T3.CLAIM_ID = T2.CLAIM_ID
GROUP BY T1.CLAIM_ID
But it would be much simpler if you just want them each as rows.
select T1.CLAIM_ID, T3.PAY_METHOD, COUNT(*)
from TB1 T3
JOIN TB2 T2 ON T3.CLAIM_ID = T2.CLAIM_ID
GROUP BY T1.CLAIM_ID, T3.PAY_METHOD
sql would be:
select T3.PAY_METHOD
, COUNT(T3.PAY_METHOD) records
from TB1 T3
JOIN TB2 T2 ON T3.CLAIM_ID = T2.CLAIM_ID
GROUP BY T3.PAY_METHOD
You can use application code to format the output.