SQL Server - Conditional OUTER APPLY - sql

Is there a way in SQL Server 2008 do to something like below?
Can the outer applied(joined) table to be specified based on a condition?
declare #bGetExtendedInfo bit
set #bGetExtendedInfo = 1
declare #param nvarchar(24)
set #param = 'CO-02-BBB'
select t1.*, t2.ID
from t1
outer apply (
case when #bGetExtendedInfo= 0
then (select 0) as ID /* dummy value */
/*really expensive query trying to avoid when extended info is not needed*/
else (select top 1 ID from tbl1 where tbl1.code = #param)
end
) t2

You can readily do this with just a join:
SELECT t1.*, t2.*
FROM t1 cross join
(SELECT *
FROM (SELECT top 1 tb10.*, 0 as bCcond src FROM tb10
UNION ALL
SELECT top 1 tb11.*, 1 as bCcond src FROM tb11
) t
WHERE #bCond = bCcond
) t2
Doing the top before the union all should also help the optimizer produce a better query plan, in the event that the tables are really complex joins.
Also, doing top without an order by is generally frowned upon. It can return different rows with different invocations, but the rows are not guaranteed to be random.

Give this a try,
DECLARE #bCond BIT
SET #bCond = 1
SELECT t1.*, t2.*
FROM t1 OUTER APPLY
(
SELECT TOP 1 *
FROM
(
SELECT *, 'a' src FROM tb10
UNION ALL
SELECT *, 'b' src FROM tb11
)s
WHERE src = CASE WHEN #bCond = 0 THEN 'a' ELSE 'b' END
) t2

Related

Multiple columns return in CASE Statement SQL

I have two tables and based on the sum of a field in TABLE1 I have to return different datasets from TABLE2:
I am trying to achieve this through a Case statement but getting an error saying subselect must have only one field.
Is there a better way to do this? simply when the sum of a column in table1 is 0 do not select anything from table2
TABLE1:
TABLE2:
MY SQL:
SELECT
CASE
WHEN SUM(transaction_unit_failed) > 0
THEN (
SELECT sale_event_nr, business_unit, transaction_nr, transaction_unit_failed_number
FROM TABLE2
)
WHEN SUM(transaction_unit_failed) = 0
THEN (
SELECT sale_event_nr, business_unit, transaction_nr, transaction_unit_failed_number
FROM TABLE2
WHERE 1 = 2
)
FROM TABLE1
select * from table2
where exists (
select 1
from table1
having sum(transaction_unit_failed) > 0
);
Similarly:
select * from table2
where (
select sum(transaction_unit_failed)
from table1
) > 0;
https://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=3f68d250bc9a3235767b86626092799e
You could certainly write it as a join if there were a compelling reason. It would eliminate the convenience of nicely using * to return only the columns from the one table.
select *
from table2 inner join (
select sum(transaction_unit_failed) as stuf
from table1
) on stuf > 0;
SELECT sale_event_nr, business_unit, transaction_nr, transaction_unit_failed_number
FROM TABLE2
WHERE (SELECT SUM(transaction_unit_failed) > 0
FROM TABLE1)

Updating the SQL query from Count(*) to EXISTS in SQL Server

I have the following SQL query,
CASE
WHEN (SELECT COUNT(*)
FROM MyTable AS Parameter
INNER JOIN Table ON Parameter.Attribute1 = Table.Attribute2
WHERE FD.DefID= Parameter.DefID AND Parameter.VTypeID = 1) = 0
THEN (
SELECT * from Table2)
ELSE
NULL
END AS Items
Basically, I would like to ensure that conditional execution is only if the query result count is 0.
How should I modify it to use EXISTS/NOT EXISTS keyword?
You can use exists to do that like so:
SELECT * from Table2
WHERE NOT EXISTS(
SELECT 1
FROM MyTable AS Parameter
INNER JOIN Table ON Parameter.Attribute1 = Table.Attribute2
WHERE FD.DefID= Parameter.DefID AND Parameter.VTypeID = 1)

Checking whether two tables have identical content

How can I check and store in variable whether two tables have identical content?
I have table variable with data like
declare #table1 table (id int)
insert into #table1 (id) values (1), (2), (3)
and as the second table I have query
select T.id from SomeTable T
inner join #table1 T1 on T.id = T1.id
the query returns data:
id
-----
1
2
In this case I need write false(0) into declare #HasAccess BIT variable.
When the query returns data:
id
-----
1
2
3
then I need write true(1) into #HasAccess
Hmmm. There are various ways.
Given that you have one column, you can do:
select (case when count(*) = 0 then 1 else 0 end)
from t1 full join
t2
on t1.id = t2.id
where t1.id is null or t2.id is null;
This checks if an id doesn't match in either table.
Another way uses union all:
select (case when count(*) = 0 then 1 else 0 end)
from (select id, sum(in_t1) as num_t1, sum(in_t2) as num_t2)
from ((select id, 1 as in_t1, 0 as in_t2 from table1) union all
(select id, 0, 1 from table2)
) tt
group by id
) tt
where num_t1 <> 1 or num_t2 <> 1;
Another option (just for fun). This will compare the entire table fields and values.
I suspect not the best option for LARGE tables
Example
Select IsIdentical = case when (Select * from Table1 Order by id For XML Raw)
= (Select * from Table2 Order by id For XML Raw)
then 1 else 0 end
EDIT - Option with Inner Join
Select IsIdentical = case when (Select * from #Table1 Order by id For XML Raw)
= (Select A.*
From SomeTable A
Join #Table1 B on A.ID=B.ID
Order By id For XML Raw)
then 1 else 0 end
Using EXCEPT:
SET #HasAccess = ISNULL(
( SELECT 0
WHERE EXISTS(
SELECT ID /* add more columns here if needed */
FROM #table1
EXCEPT
SELECT ID /* add more columns here if needed */
FROM SomeTable )), 1 )
Explanation:
Return all IDs from #table1, except those found in SomeTable
Return 0 (false) if any records have been returned by [1].
If no records returned by [1] the main query will return NULL, hence the ISNULL
Advanatages
Can easily be extended to comparisons on more than one column.

SQL Query to compare attributes stored in the same database table side by side

Let's say that we have a table like this:
entity_id attribute_name attribute_value
----------------------------------------
0 server alpha
1 server beta
0 priority 1
1 priority 2
1 comment some comment
2 server gamma
What would be the query, for PostgreSQL, that would give these results:
server alpha beta
priority 1 2
Note:
we assume that we want to compare entities with id 0 and 1 only.
if an attribute is not present for both entities it can be ignored
Try This
CREATE TABLE test1(entity_id int,attribute_name varchar(100), attribute_value varchar(100))
insert into test1
VALUES(0,'server','alpha'),
(1,'server','beta'),
(0,'priority','1'),
(1,'priority','2'),
(2,'server','gamma')
;WITH CTE as(
select attribute_name,(select STUFF((select ','+ attribute_value from test1 where entity_id in (0,1) and attribute_name=t1.attribute_name for XML path('')),1,1,'') ) as colms
from test1 t1
where entity_id in (0,1)
group by attribute_name)
select attribute_name,LEFT(colms,CHARINDEX(',',colms,1)-1) as attr_value1,RIGHT(colms,len(colms)-CHARINDEX(',',colms,1)) as attr_value2 from CTE
order by 1 desc
This should work in TSQL:
SELECT T.attribute_name, A.attribute_value, B.attribute_value
FROM theTable T
CROSS APPLY (
SELECT T0.attribute_value
FROM theTable T0
WHERE T0.entityId = 0 AND T0.attribute_name = T.attribute_name
) A
CROSS APPLY (
SELECT T1.attribute_value
FROM theTable T1
WHERE T1.entityId = 1 AND T1.attribute_name = T.attribute_name
) B
You could add more CROSS APPLYs to include results for more entities. If you aren't guaranteed to have an attribute_value for every entityId/name pair, you can replace it with OUTER APPLY (or use INNER/LEFT JOINs equivalently)
works at least in MySQL:
select attr_list.attribute_name, t0.attribute_value, t1.attribute_value
from (select distinct attribute_name from tbl) as attr_list
left join tbl as t0 on t0.attribute_name = attr_list.attribute_name
and t0.entity_id = 0
left join tbl as t1 on t1.attribute_name = attr_list.attribute_name
and t1.entity_id = 1

SQL: Select lowest value that doesn't already exist

In TableA I have an int column.
Is it possible using only a select statement to select the minimum value in the column that DOES NOT EXIST and is greater then 0?
For example, if the col has the values 1,2,9 the select statement will return 3.
If the col has 9,10,11 it will return 1.
I can achieve this using a temp table or using a loop, but I'm wondering if I can do it using just a select statement?
SELECT MIN(t1.ID+1) as 'MinID'
FROM table t1 LEFT JOIN table t2
On t1.ID+1=t2.ID
Where t2.OtherField IS NULL
select
min(nt.id)
from numbertable nt
left outer join originaldata od
on nt.id=od.id
where od.id is null
have a number table that goes from 1 to your max value (or higher)
SELECT DISTINCT x + 1 "val"
EXCEPT SELECT DISTINCT x "val"
ORDER BY "val" ASC
LIMIT 1
What about this?
SELECT Min(id)
FROM (SELECT 1 id
FROM tablea
WHERE 1 NOT IN (SELECT id
FROM tablea)
UNION
SELECT id + 1 id
FROM tablea
WHERE id + 1 NOT IN (SELECT id
FROM tablea)) AS min_ids;
try this:(Updated)
declare #dummy varchar(10) ;
set #dummy =(select top(1) id from dbo.b)
if( #dummy= '1')
begin
select top(1)l.id + 1 as start
from dbo.b as l
left outer join dbo.b as r on l.id + 1 = r.id
where r.id is null
end
else
begin
select '1'
end
Give this a try:
declare #TestTable table (
col int
)
/* Test Case 1: 1,2,9 */
insert into #TestTable
(col)
select 1 union all select 2 union all select 9
SELECT MinValue = (SELECT ISNULL(MAX(t2.col),0)+1
FROM #TestTable t2
WHERE t2.col < t1.col)
FROM #TestTable t1
WHERE t1.col - 1 NOT IN (SELECT col FROM #TestTable)
AND t1.col - 1 > 0
delete from #TestTable
/* Test Case 2: 9,10,11 */
insert into #TestTable
(col)
select 9 union all select 10 union all select 11
SELECT MinValue = (SELECT ISNULL(MAX(t2.col),0)+1
FROM #TestTable t2
WHERE t2.col < t1.col)
FROM #TestTable t1
WHERE t1.col - 1 NOT IN (SELECT col FROM #TestTable)
AND t1.col - 1 > 0
I duplicated my answer from here:
SELECT MIN(a.id) + 1 AS firstfree
FROM (SELECT id FROM table UNION SELECT 0) a
LEFT JOIN table b ON b.id = a.id + 1
WHERE b.id IS NULL
This handles all cases I can think of - including no existing records at all.
The only thing I don't like about this solution is that additional conditions have to be included twice, like that:
SELECT MIN(a.id) + 1 AS firstfree
FROM (SELECT id FROM table WHERE column = 4711 UNION SELECT 0) a
LEFT JOIN table b ON b.column = 4711 AND b.id = a.id + 1
WHERE b.id IS NULL
Please also notice the comments about locking and concurrency - the requirement to fill gaps is in most cases bad design and can cause problems. However, I had a good reason to do it: the IDs are to be printed and typed by humans and we don't want to have IDs with many digits after some time, while all the low ones are free...