Only one expression can be specified in the select list when the subquery is not introduced with EXISTS. in subquery sqlserver - sql

I want to execute this query in my database.As you can see both tables A and B has one-many relations ,but i need the latest record in B.so i here is my query :
select *,(select top 1 ResultTest ,ResultState2 from B where GasReceptionId=A.Id order by Id desc)
from A where OrganizationGasId= 4212
But i get this error
Msg 116, Level 16, State 1, Line 2
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.

You can rephrase this query as a basic join which uses an analytic function (e.g. row number) to identify the correct row's data from B to include with each record coming from the A table.
SELECT *
FROM
(
SELECT a.*, b.ResultTest, b.ResultState2,
ROW_NUMBER() OVER (PARTITION BY a.Id ORDER BY a.ID DESC) rn
FROM A a
LEFT JOIN B b
ON a.Id = b.GasReceptionId
WHERE
a.OrganizationGasId = 4212
) t
WHERE t.rn = 1;

A subquery in the SELECT clause must return exactly one column (and one or zero rows). So you can either have two subqueries:
select
a.*,
(select top 1 resulttest from b where gasreceptionid = a.id order by id desc) as test,
(select top 1 resultstate2 from b where gasreceptionid = a.id order by id desc) as state
from a
where a.organizationgasid = 4212;
Or, much better, move the subquery to the FROM clause. One way is OUTER APPLY:
select
a.*, r.resulttest, r.resultstate2
from a
outer apply
(
select top 1 resulttest, resultstate2
from b
where gasreceptionid = a.id
order by id desc
) r
where a.organizationgasid = 4212;

Related

Using Partition By in an inner join to return a single value

I have read a table and it returned a value. I now need to do an Inner Join with another table to get the next highest value available after this value in this Table.
I.e I have just returned the value 7 from a select and the Table I now need to join with contains the values;
1
5
7
11
20
I only want to return 11 in my join.
I have tried 'Row_Number () Over Partition By' but doesn't work for me because I am using an 'on (A.Number > B.Number) in the Join statement so the Row Number returned will not always be 1 for me.
Any advice?
I tried something like this;
SELECT a_number_field
FROM Table_A A
INNER JOIN (
SELECT ROW_NUMBER() OVER (
PARTITION BY another_number_field
ORDER BY another_number_field
) rn
FROM Table_B
) B
ON (b.another_number_field > a.A_number_field)
WHERE a.number_field = 7
and rn=1;
I am expecting only the value 11 to be returned to me.
Use the ROW_NUMBER with your join query as the following:
SELECT a_number_field, another_number_field
FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY A.a_number_field ORDER BY another_number_field) rn FROM
tableA A JOIN tableB B
ON B.another_number_field > A.a_number_field
) T
WHERE rn = 1 AND a_number_field = 7
See a demo.

Oracle 11G DB : Result from 'clob' type column in view changed while using the view in a where clause

I have the current query that i'm running in Oracle:
WITH viewa
AS (SELECT c.columna
FROM sometable c
LEFT JOIN othertable u
ON ( c.id = u.id )
WHERE id= '111'
ORDER BY c.created_date)
SELECT columna
FROM (SELECT rownum AS row_num,
t.*
FROM viewa t)
WHERE row_num > (SELECT CASE
WHEN ( Count(*) > 100 ) THEN Count(*) - 100
ELSE 0
END AS num
FROM viewa)
the idea is to always get the first 100 rows.
as you can see, i'm creating a view at the beginning and use it twice:
in the from and in the where.
i'm doing that so i wouldn't need to fetch the first select twice and it also make the query more readable.
notice that columna is of type CLOB!!
when i'm doing the same query with other column types its working!
so its probably something related to the clob column
The weird think is that the results that im getting are empty values even though i have values in the DB!
when i'm removing the subselect in the where i'm getting the right result:
WITH viewa
AS (SELECT c.columna
FROM sometable c
LEFT JOIN othertable u
ON ( c.id = u.id )
WHERE id = '111'
ORDER BY c.created_date)
SELECT columna
FROM (SELECT rownum AS row_num,
t.*
FROM viewa t)
WHERE row_num > 0
seems like Oracle is turning the values for the Clob column "columnA" into null when using the view in the where.
is someone familiar with that?
know how to go around it ?
i solved it with a different query but i still would like to know if Oracle does change the view while fetching from it?
thank you
Without sample data this is hard but I'm guessing the reason is you're depending on rownum. Use the FETCH clause instead to limit the number of rows.
WITH viewa
AS (SELECT c.columna
FROM sometable c
LEFT JOIN othertable u ON ( c.id = u.id )
-- an order by clause should go here
FETCH FIRST 100 ROWS ONLY)
SELECT columna
FROM viewa
But you don't need that CTE at all, just do
SELECT c.columna
FROM sometable c
LEFT JOIN othertable u ON ( c.id = u.id )
-- an order by clause should go here
FETCH FIRST 100 ROWS ONLY
Note that the "first" rows are not guaranteed to be a specific set of rows unless you explicitly add an ORDER BY clause.
Since 11g does not have FETCH FIRST, you can just use rownumber as the limiting criteria. See Example at Oracle Live
select columna, created_date
from (
select c.columna, c.created_date
from sometable c
left join othertable u
on ( c.id = u.id )
where c.id = '111'
order by c.created_date
)
where rownum <= 10;

Value present in more than one table

I have 3 tables. All of them have a column - id. I want to find if there is any value that is common across the tables. Assuming that the tables are named a.b and c, if id value 3 is present is a and b, there is a problem. The query can/should exit at the first such occurrence. There is no need to probe further. What I have now is something like
( select id from a intersect select id from b )
union
( select id from b intersect select id from c )
union
( select id from a intersect select id from c )
Obviously, this is not very efficient. Database is PostgreSQL, version 9.0
id is not unique in the individual tables. It is OK to have duplicates in the same table. But if a value is present in just 2 of the 3 tables, that also needs to be flagged and there is no need to check for existence in he third table, or check if there are more such values. One value, present in more than one table, and I can stop.
Although id is not unique within any given table, it should be unique across the tables; a union of distinct id should be unique, so:
select id from (
select distinct id from a
union all
select distinct id from b
union all
select distinct id from c) x
group by id
having count(*) > 1
Note the use of union all, which preserves duplicates (plain union removes duplicates).
I would suggest a simple join:
select a.id
from a join
b
on a.id = b.id join
c
on a.id = c.id
limit 1;
If you have a query that uses union or group by (or order by, but that is not relevant here), then you need to process all the data before returning a single row. A join can start returning rows as soon as the first values are found.
An alternative, but similar method is:
select a.id
from a
where exists (select 1 from b where a.id = b.id) and
exists (select 1 from c where a.id = c.id);
If a is the smallest table and id is indexes in b and c, then this could be quite fast.
Try this
select id from
(
select distinct id, 1 as t from a
union all
select distinct id, 2 as t from b
union all
select distinct id, 3 as t from c
) as t
group by id having count(t)=3
It is OK to have duplicates in the same table.
The query can/should exit at the first such occurrence.
SELECT 'OMG!' AS danger_bill_robinson
WHERE EXISTS (SELECT 1
FROM a,b,c -- maybe there is a place for old-style joins ...
WHERE a.id = b.id
OR a.id = c.id
OR c.id = b.id
);
Update: it appears the optimiser does not like carthesian joins with 3 OR conditions. The below query is a bit faster:
SELECT 'WTF!' AS danger_bill_robinson
WHERE exists (select 1 from a JOIN b USING (id))
OR exists (select 1 from a JOIN c USING (id))
OR exists (select 1 from c JOIN b USING (id))
;

SQL Server ROW_NUMBER Left Join + when you don't know column names

I'm writing a page that will create a query (for non-db users) and it create the query and run it returning the results for them.
I am using row_number to handle custom pagination.
How do I do a left join and a row_number in a subquery when I don't know the specific columns I need to return. I tried to use * but I get an error that
The column '' was specified multiple times
Here is the query I tried:
SELECT * FROM
(SELECT ROW_NUMBER() OVER (ORDER BY Test) AS ROW_NUMBER, *
FROM table1 a
LEFT JOIN table2 b
ON a.ID = b.ID) x
WHERE ROW_NUMBER BETWEEN 1 AND 50
Your query is going to fail in SQL Server regardless of the row_number() call. The * returns all columns, including a.id and b.id. These both have the same name. This is fine for a query, but for a subquery, all columns need distinct names.
You can use row_number() for an arbitrary ordering by using a "subquery with constant" in the order by clause:
SELECT * FROM
(SELECT ROW_NUMBER() OVER (ORDER BY (select NULL)) AS ROW_NUMBER, *
FROM table1 a
LEFT JOIN table2 b
ON a.ID = b.ID) x
WHERE ROW_NUMBER BETWEEN 1 AND 50 ;
This removes the dependency on the underlying column name (assuming none are named ROW_NUMBER).
Try this sql. It should work.
SELECT * FROM
(SELECT ROW_NUMBER() OVER (ORDER BY a.Test) AS ROW_NUMBER, a.*,b.*
FROM table1 a
LEFT JOIN table2 b
ON a.ID = b.ID) x
WHERE ROW_NUMBER BETWEEN 1 AND 50

SQL: select multiple columns based on multiple groups of minimum values?

The following query gives me a single row because b.id is pinned. I would like a query which I can give a group of ids and get the minimum valued row for each of them.
The effect I want is as if I wrapped this query in a loop over a collection of ids and executed the query with each id as b.id = value but that will be (tens of?) thousands of queries.
select top 1 a.id, b.id, a.time_point, b.time_point
from orientation_momentum a, orientation_momentum b
where a.id = '00820001001' and b.id = '00825001001'
order by calculatedValue() asc
This is on sql-server but I would prefer a portable solution if it's possible.
SQL Server ranking function should do the trick.
select * from (
select a.id, b.id, a.time_point, b.time_point,
rank() over (partition by a.id, b.id
order by calculatedValue() asc) ranker
from orientation_momentum a, orientation_momentum b
where a.id = '00820001001' and b.id between 10 and 20
) Z where ranker = 1