Oracle SQL division - sql

I have
SELECT
COUNT(*) AS a,
SUM(CASE WHEN r.hn IS NOT NULL THEN 1 ELSE 0 END) AS b,
SUM(CASE WHEN r.hn IS NULL THEN 1 ELSE 0 END) AS c,
( ____ / ____ ) AS d
FROM
x
LEFT JOIN (SELECT DISTINCT xn FROM yn) r ON x.xn = y.xn;
I need the blanks on line 4 to be the values set to 'a' and 'c', I'm not sure what the correct syntax is though.

You can't refer to column aliases in the same level of the query (except in the order by clause), so you have to either repeat the original expression as in #juergend's answer, or use an inline view:
SELECT a, b, c, a/c AS d
FROM (
SELECT
COUNT(*) AS a,
SUM(CASE WHEN y.hn IS NOT NULL THEN 1 ELSE 0 END) AS b,
SUM(CASE WHEN y.hn IS NULL THEN 1 ELSE 0 END) AS c
FROM x
LEFT JOIN (SELECT DISTINCT xn FROM yn) y ON y.xn = x.xn
);
For complicated expressions this is a bit simpler and easier to maintain - if the expression changes you only have to modify it in one place, reducing the risk of a mistake.
If you're trying to make d the ratio of nulls to the total then you just need the division reversed, as c/a; and if you wanted the percentage then100*c/a, possibly rounded or truncated to a certain precision.
And as Clockwork-Muse mentioned, since count() ignores nulls, you coudl use that instead of the two sum() calls:
SELECT a, b, c, a/c AS d
FROM (
SELECT
COUNT(*) AS a,
COUNT(y.hn) AS b,
COUNT(*) - COUNT(y.hn) AS c
FROM x
LEFT JOIN (SELECT DISTINCT xn FROM yn) y ON y.xn = x.xn
);
... or you could calculate c in the outer query too, as (b - a), though that makes the d calculation messier.

The correct syntax is to rewrite the statements again. You can't re-use alias names in the select clause.
SELECT COUNT(*) AS t,
count(r.hn) AS c,
SUM(case when r.hn IS NULL then 1 end) AS u,
count(r.hn) / SUM(case when r.hn IS NULL then 1 end) AS p
FROM h
LEFT JOIN (SELECT DISTINCT hn FROM r) r ON h.hn = r.hn;

Related

Convert SQL query to work in MS Access SQL

I'm trying to convert SQL query to work in MS-Access, is there any suggested way? thank you
SELECT
colldet.college,
COUNT(DISTINCT manuscript.p_name) AS A,
COUNT(DISTINCT CASE WHEN s_p = 'منجز' THEN p_name END) AS B,
COUNT(DISTINCT CASE WHEN s_p = 'منجز منشور' THEN p_name END) AS C,
COUNT(DISTINCT CASE WHEN s_p = 'مخطط' THEN p_name END) AS D
FROM manuscript
RIGHT OUTER JOIN colldet
ON manuscript.coll_name = colldet.college
GROUP BY colldet.college
As June7 notes, Access doesn't support COUNT DISTINCT so you'll need to make things distinct before counting. Also as June7 suggests use Iif() instead of CASE. This is a guess:
SELECT Q.College,
COUNT(Q.A1) AS A,
COUNT(Q.B1) AS B,
COUNT(Q.C1) AS C,
COUNT(Q.D1) AS D
FROM (SELECT DISTINCT colldet.college,
manuscript.p_name AS A1,
IIF(s_p = 'منجز',p_name,Null) AS B1,
IIF(s_p = 'منجز منشور',p_name,Null) AS C1,
IIF(s_p = 'مخطط',p_name,Null) AS D1
FROM manuscript
RIGHT OUTER JOIN colldet
ON manuscript.coll_name = colldet.college)
AS Q
GROUP BY Q.college

SQL Finding duplicate values in two of the three columns of each row

Let's say we have three columns: A, B, and C.
I would like to filter the results as follows:
The values of A and B are the same (duplicated) for > 1 (more than 1) row, and the value of C is always different.
In the attached image, the values that appear selected would meet the conditions mentioned above.
What I've tried:
SELECT
a.notation as A, a.gene as B, b.id as C
FROM
`db-dummy`.sgdata c
join `db-dummy`.g_info a on a.rec_id = c.gen_id
join `db-dummy`.spec_data b on b.rec_id = c.spec_id GROUP BY A, B HAVING COUNT(*) > 1;
I thought that using GROUP BY and HAVING COUNT(*) > 1 I could get the desired result, but I get the following error:
SQL Error [1055] [42000]: (conn=1632) Expression #3 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'db-dummy.b.spec_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
If you had a single table, I would suggest just using exists. But because you have a join, use window functions. If you are. looking for different values of id:
SELECT A, B, C
FROM (SELECT a.notation as A, a.gene as B, b.id as C,
MIN(b.id) OVER (PARTITION BY a.notation, a.gene) as min_id,
MAX(b.id) OVER (PARTITION BY a.notation, a.gene) as max_id
FROM `db-dummy`.sgdata c JOIN
`db-dummy`.g_info a
ON a.rec_id = c.gen_id JOIN
`db-dummy`.spec_data b
ON b.rec_id = c.spec_id
) x
WHERE min_id <> max_id;
If you are just looking for multiple rows for a given A and B, then you can use:
SELECT A, B, C
FROM (SELECT a.notation as A, a.gene as B, b.id as C,
COUNT(*) OVER (PARTITION BY a.noation, a.gene) as cnt
FROM `db-dummy`.sgdata c JOIN
`db-dummy`.g_info a
ON a.rec_id = c.gen_id JOIN
`db-dummy`.spec_data b
ON b.rec_id = c.spec_id
) x
WHERE cnt > 1;
SELECT * FROM `db-dummy`.sgdata a
LEFT JOIN
(SELECT COUNT(Id) as count, notation, gene
FROM `db-dummy`.sgdata
GROUP BY notation, gene
HAVING COUNT(id) > 1) b
on a.notation = b.notation AND a.gene = b.gene

Better way to test SQL logic gates

I wanted to test some logic gates that I wrote for a query. Glad I did because I wrote the code late in the day and it turned out to have a relatively obvious error.
I want to know if there is any way to make the code cleaner or just look better. I'm thinking the 2 areas that could use improvement are -
How I select the results. Right now this is a case statement that returns 1 or 0, but is there a way to just write something like select (x and y)
How I generate the true/false permutations. I've considered populating a single temp table with 1/0 and reusing it with aliases, but that doesn't clean up TOO much.
select x, y, z, case when (x=1 and y=1 or z=1) then 1 else 0 end as ResultOne, case when (x=1 and (y=1 or z=1)) then 1 else 0 end as ResultTwo
from
( select 1 x
union
select 0 x
) as A
inner join
( select 1 y
union
select 0 y
) as B on 1=1
inner join
( select 1 z
union
select 0 z
) as C on 1=1
order by x,y,z
You can use table valued constructors for something like this pretty easily. Much simpler than all those joins to subqueries. Although with such a small amount of data I don't think it makes much difference. And use a cross join instead of 1 = 1. That is what they are for.
select a.x
, b.y
, c.z
, case when (a.x = 1 and b.y = 1 or c.z = 1) then 1 else 0 end as ResultOne
, case when (a.x = 1 and (b.y = 1 or c.z = 1)) then 1 else 0 end as ResultTwo
from (values(0),(1)) a(x)
cross join (values(0),(1)) b(y)
cross join (values(0),(1)) c(z)
Without table valued constructors, with some math.
select a.x
,b.y
,c.z
,case when (a.x + b.y = 2 or c.z = 1) then 1 else 0 end ResultOne
,case when (a.x = 1 and b.y + c.z >= 1) then 1 else 0 end ResultTwo
from (select 1 x union select 0) a
cross join (select 1 y union select 0) b
cross join (select 1 z union select 0) c;

sql function case returns more than one row

Going to use this query as a subquery, the problem is it returns many rows of duplicates. Tried to use COUNT() instead of exists, but it still returns a multiple answer.
Every table can only contain one record of superRef.
The below query I`ll use in SELECT col_a, [the CASE] From MyTable
SELECT CASE
WHEN
EXISTS (SELECT 1 FROM A WHERE
A_superRef = myTable.sysno AND A_specAttr = 'value')
THEN 3
WHEN EXISTS (SELECT 1 FROM B
INNER JOIN С ON С_ReferenceForB = B_sysNo WHERE C_superRef = myTable.sysno AND b_type = 2)
THEN 2
ELSE (SELECT C_intType FROM C
WHERE C_superRef = myTable.sysno)
END
FROM A, B, C
result:
3
3
3
3
3
3...
What if you did this? Because Im guessing you are getting an implicit full outer join A X B X C then running the case statement for each row in that result set.
SELECT CASE
WHEN
EXISTS (SELECT 1 FROM A WHERE
A_superRef = 1000001838012)
THEN 3
WHEN EXISTS (SELECT 1 FROM B
INNER JOIN С ON С_ReferenceForB = B_sysNo AND C_superRef = 1000001838012 )
THEN 2
ELSE (SELECT C_type FROM C
WHERE C_superRef = 1000001838012)
END
FROM ( SELECT COUNT(*) FROM A ) --This is a hack but should work in ANSI sql.
--Your milage my vary with different RDBMS flavors.
DUAL is what I needed, thanks to Thorsten Kettner
SELECT CASE
WHEN
EXISTS (SELECT 1 FROM A WHERE
A_superRef = 1000001838012)
THEN 3
WHEN EXISTS (SELECT 1 FROM B
INNER JOIN С ON С_ReferenceForB = B_sysNo AND C_superRef = 1000001838012 )
THEN 2
ELSE (SELECT C_type FROM C
WHERE C_superRef = 1000001838012)
END
FROM DUAL

How to find rows that have one equal value and one different value from the table

I have the following table:
ID Number Revision
x y 0
x y 1
z w 0
a w 0
a w 1
b m 0
b m 0
I need to return rows that for the same Number thare are more then one ID with the same Revision.Number can be "Null" and I don't need those values.
The output should be:
z w 0
a w 0
I have tried the following query:
SELECT a.id,a.number,a.revision,
FROM table a INNER JOIN
(SELECT id, number, revision FROM table where number > '0'
GROUP BY number HAVING COUNT(*) > 1
) b ON a.revision = b.revision AND a.id != b.id
A little addition- I have rows in my table with the same Number, ID and Revision- I don't need those rows in my query to be displayed!
It is not working! Please help me to figure out how to fix it.
Thanks.
Select t.Id,s.number,t.revision
from (Select number,count(*) 'c'
from table t1
where revision=0
group by number
having count(*) > 1
) s join table t on t.number= s.number
where revision = 0
Another simple approach:
SELECT DISTINCT b.id, b.Number, b.Revision
FROM tbl a
INNER JOIN tbl b
ON a.ID != b.ID AND a.Number = b.Number AND a.Revision = b.Revision;
This is tested in MySql 5, syntax might differ slightly.
You are not that far away with your query:
SELECT a.id,a.number,a.revision
FROM table a
JOIN (
-- multiple id for the same number and revision
SELECT number, revision
FROM table
GROUP BY number, revision
HAVING COUNT(*) > 1
) b
ON a.revision = b.revision
AND a.number = b.number
Untested, but you should get the idea. If your sql-server is a resent version you can solve this with OLAP functions as well.
To filter out rows where the whole row is duplicated we can select only unique rows via group by and having:
SELECT a.id,a.number,a.revision
FROM table a
JOIN (
-- multiple id for the same number and revision
SELECT number, revision
FROM table
GROUP BY number, revision
HAVING COUNT(*) > 1
) b
ON a.revision = b.revision
AND a.number = b.number
GROUP BY a.id,a.number,a.revision
HAVING COUNT(1) = 1