select a field from one row as a field from another SQL - sql

Sorry for the confusing title. I was having trouble searching for the solution I am looking for because I do not know how to summarize it in a few words.
I have a single table, table_name, with columns Indicator, ID, and Num. The Indicator is either 0 or 1 and the ID can exist up to 2 times. If the ID number exists twice, one of the indicator is 0 and the other is 1 and if the ID exists once, its indicator is 0. My query needs to be able to return 0 if the indicator for a row is 0 and Num from the matching ID with indicator 0 if the indicator is 1.
Indicator----ID-----Num
1-------------01----3000
0-------------01----4000
0-------------02----5000
0-------------03----100
1-------------04----400
0-------------04----200
RESULTS of Query
4000
0
0
0
200
0

This is tricky, because you want to be sure not to lose any rows. For that reason, I'm doing this with a nested select statement:
select (case when indicator = 0 then 0
else (select t2.num from table_name t2 where t2.id = t.id and t2.indicator = 0)
end) as Val
from table_name t
Here is an example of it working (assuming your database supports with):
with table_name as (
select 1 as indicator, 1 as id, 3000 as num union all
select 0, 1, 4000 union all
select 0, 2, 5000 union all
select 0, 3, 100 union all
select 1, 4, 400 union all
select 0, 4, 200
)
select (case when indicator = 0 then 0
else (select t2.num from table_name t2 where t2.id = t.id and t2.indicator = 0)
end) as Val
from table_name t

select case when oneId.id is null the 0 else zero.num end case
from table1 zero
left join table1 oneId
on zero.id = oneId.id
and 1 = oneId.indicator
where zero.indicator = 0

Try this:
SELECT IF(t1.indicator = 0 OR t2.Num IS NULL, 0, t2.Num)
FROM table_name as t1
LEFT JOIN table_name as t2 ON(
t1.ID = t2.ID
AND t1.indicator != t2.indicator
)
http://sqlfiddle.com/#!2/61623a/3

Related

How To Convert Sql to Oracle Query

I have sql query like :-
SELECT CASE WHEN (NOT EXISTS(SELECT NULL
FROM [Customers] AS t0
WHERE NOT (LEN(t0.[ContactName]) > 0)
)) THEN 1 ELSE 0 END AS [value]
Please let me know how I can convert to oracle query?
Thanks in advance.
Joon
In Oracle, an empty string and NULL are identical so LENGTH( NULL ) and LENGTH( '' ) will both give NULL and not 0.
So your query would be:
SELECT CASE
WHEN NOT EXISTS(
SELECT 1
FROM Customers
WHERE ContactName IS NULL
)
THEN 1
ELSE 0
END AS value
FROM DUAL
Update:
What if I want to find out all records has length of contactname bigger than 3
SELECT CASE
WHEN NOT EXISTS(
SELECT 1
FROM Customers
WHERE LENGTH( ContactName ) <= 2
OR ContactName IS NULL
)
THEN 1
ELSE 0
END AS value
FROM DUAL
or
SELECT CASE
WHEN NOT EXISTS(
SELECT 1
FROM Customers
WHERE COALESCE( LENGTH( ContactName ), 0 ) <= 2
)
THEN 1
ELSE 0
END AS value
FROM DUAL
Note: to use an index you will need a function-based index on LENGTH( ContactName ) or COALESCE( LENGTH( ContactName ), 0 ) for the respective examples.
If your SQL checks "if there exists an empty contact name in the customers table" then you can use the oracle queries below.
SELECT DECODE(cnt, 0, 0, 1)
FROM (SELECT COUNT(1) cnt
FROM customers
WHERE NOT NVL(LENGTH(contact_name), 0) > 0);
or
SELECT DECODE(cnt, 0, 0, 1)
FROM (SELECT COUNT(1) cnt
FROM customers
WHERE contact_name IS NULL);
to avoid null error use nvl function:
SELECT
CASE
WHEN (NOT EXISTS
(SELECT NULL FROM Customers t0 WHERE NOT NVL(LENGTH(t0.ContactName),0) > 0
))
THEN 1
ELSE 0
END val
FROM DUAL;
I got it like that:
SELECT CASE WHEN (NOT EXISTS(SELECT NULL FROM Customers t0 WHERE NOT
LENGTH(t0.ContactName) > 0)) THEN 1 ELSE 0 END val
FROM DUAL
thanks all

Returning only id's of records that meet criteria

I need to return distinct ID's of records which meet following conditions :
must have records with field reason_of_creation = 1
and must NOT have records with field reason_of_creation = 0 or null
in the same time.
While i was able to do it, i keep wondering is there more elegant (even recommended) way of doing it.
Here is anonymized version of what i have :
select distinct st.some_id from (
select st.some_id, wanted.wanted_count as wanted, unwanted.unwanted_count as unwanted
from some_table st
left join (
select st.some_id, count(st.reason_of_creation) as wanted_count
from some_table st
where st.reason_of_creation=1
group by st.some_id
) wanted on wanted.some_id = st.some_id
left join (
select st.some_id, count(st.reason_of_creation) as unwanted_count
from some_table st
where st.reason_of_creation=0
group by st.some_id
) unwanted on unwanted.some_id = st.some_id
where wanted.wanted_count >0 and (unwanted.unwanted_count = 0 or unwanted.unwanted_count is null)
) st;
Sample data :
some_id reason_of_creation
1 1
1 0
2 1
3 null
4 0
4 1
5 1
desired result would be list of records with some_id = 2, 5
It seems to me your query is overkill,all you need is some post aggregation filtering
SELECT some_id FROM t
GROUP BY some_id
HAVING SUM(CASE WHEN reason_of_creation = 1 THEN 1 ELSE 0 END)>0
AND SUM(CASE WHEN reason_of_creation = 0 OR reason_of_creation IS NULL THEN 1 ELSE 0 END)=0
I think that more elegant query exists and it is based on assumption what reasoson_of_crdeation field is integer, so minimal possible it's value, which greater than 0 is 1
This is for possible negative values for reasoson_of_crdeation:
select someid from st
where reasoson_of_crdeation != -1
group by someid
having(min(nvl(abs(reasoson_of_crdeation), 0)) = 1)
or
select someid from st
group by someid
having(min(nvl(abs(case when reasoson_of_crdeation = -1 then -2 else reasoson_of_crdeation end), 0)) = 1)
And this one in a case if reasoson_of_crdeation is non-negative integer:
select someid from st
group by someid
having(min(nvl(reasoson_of_crdeation, 0)) = 1)

Select statement to return constant when no records found in table in SQL Server 2008

I am have a table with data and now i need to return zero in select statement if there is no records in table for example. I need to use it in Stored Procedure.
-- If no records exists in below select statement
SELECT ID,Text,Date FROM tblData WHERE ID = 12
IF (##ROWCOUNT = 0)
BEGIN
SELECT -5 AS ID
END
Output:
ID Text Date
ID
-5
Expected output
ID
-5
If you want to return 1 row even when there is no match, you can use aggregation:
SELECT (CASE WHEN COUNT(*) = 0 THEN -5 ELSE MAX(ID) END) as ID
FROM tblData
WHERE ID = 12;
I always use an Exists statment.
if exists(SELECT ID FROM tblData WHERE ID = 12)
select 0 as RowsExist
else
select 1 as RowsExist
For a single scalar value you could use something like;
SELECT ISNULL((SELECT ID FROM tblData WHERE ID = 12), 0) as ID
Rhys
SELECT (CASE WHEN Ta.ID IS NULL THEN TBL.ID
ELSE Ta.ID END) AS ID,Ta.Text,Ta.Date
FROM (VALUES(-5)) AS TBL(ID)
LEFT JOIN
(
SELECT ID,Text,Date FROM tblData WHERE ID = 12
)
AS Ta ON Ta.ID = Ta.ID

sql Select id that matches combination of column

I have a token table
id | status
------------
1 | taken
1 | used
1 | deleted
2 | taken
2 | deleted
3 | taken
I need to count how many tokens are used ( in use or used).
If a token is taken and deleted without being used then it should not be counted.
So sql would be sth like
SELECT count(*) if the id's status is not (taken & deleted)
The desired number of used token in above example is 2 as
id 1 has been taken used and deleted -> count it
id 3 has been taken -> count it
id 2 has been taken and deleted without being used -> do not count it
A little bit verbose but efficient and still readable and maintainable:
SELECT COUNT(DISTINCT id)
FROM dbo.Token t
WHERE EXISTS
(
SELECT 1 FROM dbo.Token t1
WHERE t.id = t1.id
AND t1.status = 'used'
)
OR
(
EXISTS(
SELECT 1 FROM dbo.Token t1
WHERE t.id = t1.id
AND t1.status = 'taken'
)
AND NOT EXISTS(
SELECT 1 FROM dbo.Token t1
WHERE t.id = t1.id
AND t1.status = 'deleted'
)
)
Demo
Use aggregation and a having clause to get the list of eligible ids:
SELECT id
FROM token t
GROUP BY id
HAVING SUM(case when status = 'taken' then 1 else 0 end) > 0 or
SUM(case when status = 'used' then 1 else 0 end) > 0;
To get the count, use a subquery or CTE:
SELECT COUNT(*)
FROM (SELECT id
FROM token t
GROUP BY id
HAVING SUM(case when status = 'taken' then 1 else 0 end) > 0 or
SUM(case when status = 'used' then 1 else 0 end) > 0
) t
Try this:
SELECT SUM(CASE WHEN (CHARINDEX('used', data.status) > 0) OR (data.status = 'taken') THEN 1 ELSE 0 END) as [count]
FROM
(
SELECT DISTINCT id, (SELECT STUFF((SELECT Distinct ',' + status
FROM token a
WHERE a.id = b.id
FOR XML PATH (''))
, 1, 1, '')) as status
FROM token b
) data
Demo
You need to be able to take into account all three conditions, so a naive approach would be to just compare each three with a case statement:
WITH grouped as
(
select id from #uses group by id
)
select grouped.id,
used =
CASE WHEN used.id is not null THEN 'YES'
WHEN taken.id is not null and deleted.id is null THEN 'YES'
ELSE 'NO'
END
from grouped
left join #uses taken on grouped.id = taken.id
and taken.use_status = 'taken'
left join #uses used on grouped.id = used.id
and used.use_status = 'used'
left join #uses deleted on grouped.id = deleted.id
and deleted.use_status = 'deleted'
The case statement will stop whenever the condition is met, so you only need to WHEN's and an ELSE to meet the conditions.
This is a naive approach, though, and assumes that you only ever have one row per id and use status type. You'd have to do some additional work if that wasn't the case.
if token has been taken and used -> do not count it
SELECT
SUM(DECODE(status, 'taken', 1, 0)) +
SUM(DECODE(status, 'used', 1, 0)) -
SUM(DECODE(status, 'deleted', 1, 0))
FROM
token t
WHERE
status <> 'used' OR
EXISTS(SELECT 1 FROM token t2 WHERE t2.id = t.id and t2.status = 'deleted')
if token has been taken and used -> count it
SELECT
COUNT(1)
FROM
token t
WHERE
status = 'taken' AND
(
EXISTS(SELECT 1 FROM token t2 WHERE t2.id = t.id and t2.status = 'used') OR
NOT EXISTS(SELECT 1 FROM token t2 WHERE t2.id = t.id and t2.status = 'deleted')
)
Coming back to this question, one solution could be with using Pivot
SELECT COUNT(id)
FROM (
SELECT id, status FROM Token
) src
PIVOT
(
COUNT(status) FOR status IN ([taken], [used], [deleted])
) pvt
WHERE (taken = 1 AND deleted = 0)OR (used = 1)
DEMO

sql query sum count

I have a table
id date state
1 10.01.01 reg
2 08.01.01 reg
3 05.01.01 check
4 02.01.01 check
5 01.01.01 reg
and want to show result like this
count reg
5 1
e.g sum of "reg" statuses should be counted only if the previous status was "check".
Please, help me or give the right direction to solve it
In SQL Server 2012 and above you could use LAG to access the previous row value:
SQL SERVER 2012
;WITH MyCTE AS
(
select id, date,state,lag(state,1) over(order by id) as prevstate
from Table1
)
SELECT COUNT(*),SUM(CASE WHEN state = 'reg' AND prevstate = 'check' THEN 1 ELSE 0 END)
FROM MyCTE
Fiddler Demo
ORACLE
With T AS
(
select "id", "date", "state", lag("state",1) over(order by "id") as "prevstate"
from Table1
)
SELECT COUNT(*),SUM(CASE WHEN "state" = 'reg' AND "prevstate" = 'check' THEN 1 ELSE 0 END)
FROM T
Fiddler Demo
MySQL
SELECT COUNT(*),SUM(CASE WHEN state = 'reg' AND prevstate = 'check' THEN 1 ELSE 0 END)
FROM
(
SELECT T1.ID,T1.Date,T1.state,T2.state AS prevstate
FROM Table1 T1
LEFT JOIN Table1 T2
ON T1.ID - 1 = T2.ID
) AS T
Fiddler Demo
Actually the third case would work in prety much everything.