SQL check link between two records (CASE, WHEN, THEN) - sql

I Have in table some records:
ID Services
2 A
2 C
2 C1
2 D2
I`m trying make query that will be select a link between services.
For example: If for ID 2 exists Services C then check if exist Service C1, result Yes or No.
SELECT a. ID, a.service,
CASE
WHEN (a.service ='C') = (a.service = 'C1') THEN 'Yes'
ELSE 'No'
END
FROM t1 a

Try this query:
SELECT *
FROM yourTable t1
WHERE NOT EXISTS (SELECT 1 FROM yourTable t2
WHERE (t2.Services LIKE t1.Services + '%' OR
t1.Services LIKE t2.Services + '%') AND
t1.ID = t2.ID AND t1.Services <> t2.Services);
This returns A and D2 only.
Demo

Hmm... what about this? But I now have problem with checking relationship for each ID independently...
SELECT a. ID, a.service,
CASE
WHEN a.service IN ('C','C1') THEN 'Yes'
ELSE 'No'
END
FROM t1 a

If I understand correctly, you can use aggregation:
SELECT ID,
(CASE WHEN SUM(CASE WHEN service = 'C' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN service = 'C1' THEN 1 ELSE 0 END) > 0
THEN 'Yes' ELSE 'No'
END) as c_c1_flag
FROM t1
GROUP BY ID;
The SUM(CASE . . . ) counts the number of rows that match the conditions. The > 0 simply says that at least one row exists.

Related

Sql query to create following tables

use the below table 1 to generate table 2
Table 1
Col1 A B C
------------------
N1 1 0 0
N2 0 1 0
N3 1 0 0
Table 2
output
new_col
-------
N1 A
N2 B
N3 A
Also how to use Table 2 to generate table 1 above
Following SQL query can be help to get requested output:
SELECT Col1,IF(A=1,'A',IF(B=1,'B','C')) AS result FROM `table_name`;
You can use conditinal with CONCAT() Function :
SELECT CONCAT(col1,' ',
CASE WHEN A = 1 THEN 'A' ELSE '' END,
CASE WHEN B = 1 THEN 'B' ELSE '' END,
CASE WHEN C = 1 THEN 'C' ELSE '' END)
FROM table1
provided you're on a DBMS with brand name such as MySQL, PostGRES, SQL Server. As an example, Oracle DB won't allow using more than two arguments for CONCAT() Function.
In order to create table2, use for most of the DBMS :
CREATE TABLE table2 AS
SELECT CONCAT(col1,' ',
CASE WHEN A = 1 THEN 'A' ELSE '' END,
CASE WHEN B = 1 THEN 'B' ELSE '' END,
CASE WHEN C = 1 THEN 'C' ELSE '' END) AS new_col
FROM table1
except for SQL Server in which prefer using :
SELECT CONCAT(col1,' ',
CASE WHEN A = 1 THEN 'A' ELSE '' END,
CASE WHEN B = 1 THEN 'B' ELSE '' END,
CASE WHEN C = 1 THEN 'C' ELSE '' END) AS new_col
INTO table2
FROM table1
In order to implement a reverse engineering(go back to original table), you need to consider the dialectics for each seperate database to handle string values. Assume you're using MySQL DB, then consider using :
CREATE TABLE table3 AS
SELECT SUBSTRING(new_col,1,instr(new_col,' ')-1) AS col1,
CASE WHEN instr(new_col,'A') > 0 THEN 1 ELSE 0 END AS A,
CASE WHEN instr(new_col,'B') > 0 THEN 1 ELSE 0 END AS B,
CASE WHEN instr(new_col,'C') > 0 THEN 1 ELSE 0 END AS C
FROM table2
where I used a different table name (table3), since table1 already exists.
Demo
Btw, if Oracle DB is the case, then use :
SELECT CONCAT(CONCAT(col1,' '),
CASE WHEN A = 1 THEN 'A' ELSE '' END||
CASE WHEN B = 1 THEN 'B' ELSE '' END||
CASE WHEN C = 1 THEN 'C' ELSE '' END) AS new_col
FROM table1
You can use one case statement. This works in Oracle.
SELECT col1
|| CASE
WHEN A = 1
THEN 'A'
WHEN B = 1
THEN 'B'
WHEN C = 1
THEN 'C'
END
NEW_COL
FROM table1
It is unclear what you want when there are multiple "1"s in a row. This is a simple solution:
select id, A from t where A = 1
union all
select id, B from t where B = 1
union all
select id, C from t where C = 1;
In databases that support lateral joins, I would recommend:
select t.id, v.which
from t cross join lateral
(values ('A', t.A), ('B', B), ('C', C)
) v(which, val)
where val = 1;

CASE in sub query with SELECT

This is the query that I've written
SELECT id, name, (SELECT is_enable FROM customers WHERE id=table_one.id) AS some_tag FROM table_one;
The above query returns the date like this
ID NAME SOME_TAG
4 name 1 0
3 name 2 0
1 name 3 1
I'm trying to fit in a CASE in the above query so that I get the value of SOME_TAG as "Yes" when 1 and "No" when 0 but no luck so far. Any help/leads will be appreciated! Thanks!
So far I tried this
select id, name, (select is_enable case when is_enable is not null then "No" else "Yes" end from customers where id=table_one.id) as some_tag from table_one;
You can use a join instead of a sub-query... and then this is how'd the case would work.
SELECT
t.id,
t.name,
case when c.is_enabled = 1 then 'Yes' else 'No' end
from
table_one t
left join customers c on
c.id = t.id
Below code also works for your problem
SELECT
id,
name,
(CASE is_enable
WHEN 1 THEN 'YES'
WHEN 0 THEN 'NO'
END) AS TAG
FROM ( SELECT id, name, (SELECT is_enable FROM customers WHERE id=table_one.id) AS some_tag FROM table_one );

Case SQL If there is no record between dates

I'm new to SQL and am struggling with a case.
I would like to make the case where if an account (account_ID) doesn't have a record (ON billing_id) between current_date-302 and current_date-62 THEN MARK WITH A "1"
Query below:
Thanks in advance
SELECT
billing_date_local_time
,account_id
,contract_owner_name
,date_first_feature_partner
,deal_starts_at
,contract_id
,new_partner_type
,sum(voucher_sold) AS Vouchers
,sum(gross_bookings_local) AS GB
,sum(gross_revenue_local) AS GR
,is_G2
,Case when billing_date_local_time between current_date-302 and current_date-62 = 0 THEN 'YES' ELSE 'NO' End
FROM EMEA_ANALYTICS.eu_deal_flat
WHERE
country_id = 206
and billing_date_local_time between current_date-400
and current_date-2
GROUP BY 1,2,3,4,5,6,10,11
You'll need to do a correlated subquery; something like this:
select
a.billing_date_local_time
,a.account_id
,...
, CASE WHEN EXISTS (SELECT * FROM EMEA_ANALYTICS.eu_deal_flat b WHERE a.account_id = b.account_id AND b.billing_date_local_time between current_date-302 and current_date-62 ) THEN 'YES' ELSE 'NO' END
from
FROM EMEA_ANALYTICS.eu_deal_flat a
WHERE ...
You need to apply an aggregate function like this:
min(case when billing_date_local_time
between current_date-302 and current_date-62
then 0
else 1
end)

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

counting records on the same table with different values possibly none sql server 2008

I have a inventory table with a condition i.e. new, used, other, and i am query a small set of this data, and there is a possibility that all the record set contains only 1 or all the conditions. I tried using a case statement, but if one of the conditions isn't found nothing for that condition returned, and I need it to return 0
This is what I've tried so far:
select(
case
when new_used = 'N' then 'new'
when new_used = 'U' then 'used'
when new_used = 'O' then 'other'
end
)as conditions,
count(*) as count
from myDB
where something = something
group by(
case
when New_Used = 'N' then 'new'
when New_Used = 'U' then 'used'
when New_Used = 'O' then 'other'
end
)
This returns the data like:
conditions | count
------------------
new 10
used 45
I am trying to get the data to return like the following:
conditions | count
------------------
new | 10
used | 45
other | 0
Thanks in advance
;WITH constants(letter,word) AS
(
SELECT l,w FROM (VALUES('N','new'),('U','used'),('O','other')) AS x(l,w)
)
SELECT
conditions = c.word,
[count] = COUNT(x.new_used)
FROM constants AS c
LEFT OUTER JOIN dbo.myDB AS x
ON c.letter = x.new_used
AND something = something
GROUP BY c.word;
try this -
DECLARE #t TABLE (new_used CHAR(1))
INSERT INTO #t (new_used)
SELECT t = 'N'
UNION ALL
SELECT 'N'
UNION ALL
SELECT 'U'
SELECT conditions, ISNULL(r.cnt, 0) AS [count]
FROM (
VALUES('U', 'used'), ('N', 'new'), ('O', 'other')
) t(c, conditions)
LEFT JOIN (
SELECT new_used, COUNT(1) AS cnt
FROM #t
--WHERE something = something
GROUP BY new_used
) r ON r.new_used = t.c
in output -
new 2
used 1
other 0
You can do it as a cross-tab:
select
sum(case when new_used = 'N' then 1 else 0 end) as N,
sum(case when new_used = 'U' then 1 else 0 end) as U,
sum(case when new_used = 'O' then 1 else 0 end) as Other
from myDB
where something = something