Calculate conversion rate with the specified conditions - sql

Here is my sample data table:
ID
Status
1
New
1
Processed
2
New
2
Processed
3
New
3
Processed
4
Processed
5
New
What I am trying to solve here is calculate the conversion rate from Status 'New' to Status 'Processed'. From the dataset, only ID no.1,2 and 3 fulfilled the requirements of my problem, and ID no.4 and 5 do not have both stages. So by theory, the conversion rate should be 3/5 * 100% = 60%. How can I select the data in order to calculate the IDs that have both 'New' and 'Processed' status.
This is the code that I have tried but I know its wrong since it extracts all the IDs with no link between it.
SELECT 'Conversion rate from Assigned enquiry to In progess leads' as 'Name', round((a.processed / b.new),3) * 100 as 'Answer'
FROM
(
SELECT cast(count(ID)as float) as processed
from table1
WHERE STATUS_ID = 'Processed'
) as a
cross join
(
SELECT cast(count(ID)as float) as new
from table_1
WHERE STATUS_ID = 'NEW'
) as b

We can use conditional aggregation here:
WITH cte AS (
SELECT CASE WHEN COUNT(CASE WHEN Status = 'New' THEN 1 END) > 0 AND
COUNT(CASE WHEN Status = 'Processed' THEN 1 END) > 0
THEN 1 ELSE 0 END AS cnt
FROM yourTable
GROUP BY ID
)
SELECT 100.0 * SUM(cnt) / COUNT(*)
FROM cte;

Related

Grouping by id and looking at another column in a particular order to see if the id group satisfies a particular condition

customer_id
transaction success
1
Failed
2
Complete
1
Failed
1
Complete
3
Failed
2
Failed
3
Complete
3
Failed
3
Failed
3
Complete
Essentially I want to write a statement to identify if the customer has had a completed transaction after having had a failed transaction sometime before. So in this example, customer 1 and customer 2 would be satisfy this. Assume that there is an added timestamp column next to transaction success.
The resulting table should look like this:
customer_id
returning_success
1
True
2
False
3
True
Assuming that is not important if the Complete was after or prior to the Cancellation, you can LEFT JOIN the table with a subquery that only takes the completes. If the result is NULL, then is not have a complete state. Otherwise is true.
As you don't provide your DBMS (Please read: Why should I "tag my RDBMS"?) we take in consideration IFNULL but this can change in other DBMS: https://www.w3schools.com/sql/sql_isnull.asp
SELECT
yt.customer_id,
IFNULL(completes.customer_id,'false','true') as returning_success
FROM
yourtable yt
LEFT JOIN
(
SELECT
customer_id
FROM
yourTable
WHERE transaction_success = 'Complete') completes
ON completes.customer_id = yt.customer_id
 If you just need customers that had had both succesfull and faild transactions, you should implement this:
select customer_id, case when sum(case
when transaction='Faild'
then 1
else 0 end)>0
and
sum(case
when transaction='Complete'
then 1
else 0 end)>0
then 'True'
else 'False' end
returning_success
from table_
group by customer_id
 If you actually do have some timestamp column:
select nvl(c.customer_id, f.customer_id) customer_id
, case when last_complete_time is null
or first_fail_time is null
or first_fail_time>last_complete_time
then 'False'
else 'True' end
returning_success
from (
select customer_id, max(time_) last_complete_time
from table_
group by customer_id
where transaction='Complete'
) c
full join (
select customer_id, min(time_) first_fail_time
from table_
group by customer_id
where transaction='Fail'
) f on c.customer_id=f.customer_id
 You also can use this query to filter all True cases and then just union or join the rest:
select f.customer_id, 'True'
from (
select customer_id, max(time_) last_complete_time
from table_
group by customer_id
where transaction='Complete'
) c
join (
select customer_id, min(time_) first_fail_time
from table_
group by customer_id
where transaction='Fail'
) f on c.customer_id=f.customer_id
where first_fail_time<last_complete_time

Get a particular record based on a condition in SQL

My requirement is to get id for missing status from SQL table. I will get a list of status for each id, say A,B,C,D. In a scenario, I have to check status B exists or not. Table gets updated everyday and each time new Id will be created
Conditions,
If status A exists and other statuses such as C and D does not
exists, then don't need to get id.
If status A and B exists and other statuses such as C or D does not exists, then don't need to get id .
If status A exists and B not exists, other
statuses such as C or D exists, then I should get the id of that
record
If status A and B exists, other
statuses such as C or D exists (all status exists), then I don't need to get the id of that
record
Table1:
Id StatusCode
1 A
1 C
2 A
2 B
2 C
3 A
3 C
3 D
How do I get Id 1 and 3 using SQL query?, Seems simple but as I am new to SQL I could not able to get it in SQL.
select statement in this screenshot works fine when there is only one id, it fails on multiple id. I tried many other way, but no use
Try this
SELECT DISTINCT ID
FROM T1
WHERE Statuscode = 'A' AND ID NOT IN (SELECT ID FROM T1 WHERE Statuscode = 'B' )
AND (ID IN (SELECT ID FROM T1 WHERE Statuscode = 'C' ) OR ID IN (SELECT ID FROM T1 WHERE Statuscode = 'D' ))
FIDDLE DEMO
Also, To correct Gordon Linoff's answer, we need to add one more where criteria there
SELECT Id
FROM T1
GROUP BY Id
HAVING SUM(CASE WHEN Statuscode = 'A' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN Statuscode = 'B' THEN 1 ELSE 0 END) = 0 AND
SUM(CASE WHEN Statuscode IN ('C', 'D') THEN 1 ELSE 0 END) > 0;
FIDDLE DEMO
This answers the original version of the question.
I think you can use aggregation:
select id
from t
group by id
having sum(case when status = 'A' then 1 else 0 end) > 0 and
sum(case when status in ('C', 'D') then 1 else 0 end) > 0;
SELECT id
FROM t
GROUP BY
Id
HAVING MAX(status) = CHAR(64 + COUNT(*))
--char(64+1) = A, char(64+2) = B etc
The logic behind this is that it will take all count the same types of id. So if you have 3 rows you will need abc. If you have an id with 4 rows you will have ABCD. Generally the max status should always be the same as the number of rows.
This is true of course if you have no duplicate between id and status code.
select distinct id from t where t.statuscode = 'C' or t.statuscode = 'D' group by t.id

How to group and get count of rows based on value of one column in sql server

I have a table of requests with columns RequestType,status .Status column values can be in-progress,complated etc.
I would like to get the list like
RequestType In-Progress Completed Total
Type1 10 5 15
Type2 10 10 20
I tried with group by using the 2 columns( RequestType,status) ,but it does not give me the exact result.
Please help me with the sql query.
Thanks in advance
Subin
One way to do it is using conditional aggrigation:
SELECT RequestType,
SUM(CASE WHEN Status = 'In-Progress' THEN 1 ELSE 0 END) As 'In-Progress',
SUM(CASE WHEN Status = 'Completed' THEN 1 ELSE 0 END) As 'Completed',
COUNT(Status) As 'Total'
FROM TableName
WHERE Status IN('In-Progress', 'Completed')
GROUP BY RequestType
Use PIVOT
select *, [In-Progress]+[Completed] total
from TableName
pivot ( count(status) for status in ([In-Progress], [Completed])) as p

SQL Server Completion Percentage Category

im a bit new to sql server, so hopefully this isnt something too convoluted. if i have a table with a bunch of data that shows different records that have been complete or not...
TABLE 1
ID CATEGORY COMPLETE
1 reports yes
2 reports no
3 processes no
4 processes yes
5 reports no
6 events yes
...what would be the best way of creating a new field that would show the percentage complete for every category?
TABLE 2
ID CATEGORY PERCENTAGE
1 events 100%
2 processes 50%
3 reports 33%
any help would be greatly appreciated, thank you.
group by category column and use conditional sum to get only complete = 'yes' cases in the numerator.
select category,
100 * 1.0 * sum(case when complete = 'yes' then 1 else 0 end)/count(*) as pct
from tablename
group by category
You can use windowed functions and PARTITION BY Category:
SELECT DISTINCT Category,
[percentage] = ROUND(100 * SUM(CASE complete WHEN 'yes' THEN 1.0 ELSE 0.0 END)
OVER (PARTITION BY Category)/
COUNT(*) OVER (PARTITION BY Category),0)
FROM #tab;
LiveDemo
With insert to second table:
SELECT DISTINCT
[id] = IDENTITY(INT, 1,1)
,category
,[percentage] = ROUND(100 * SUM(CASE complete WHEN 'yes' THEN 1.0 ELSE 0.0 END)
OVER (PARTITION BY CATEGORY)/
COUNT(*) OVER (PARTITION BY Category),0)
INTO #table2
FROM #tab
ORDER BY [percentage] DESC;
SELECT *
FROM #table2;
LiveDemo2
I think the simplest approach is to use avg():
select category,
avg(case when complete = 'yes' then 100.0 else 0 end) as pct
from tablename
group by category;
If you want this as a number with a percentage, you need a bit more string manipulation:
select category,
str(avg(case when complete = 'yes' then 100.0 else 0 end)) + '%' as pct
from tablename
group by category;
However, I would recommend keeping the value as a number.

SQL Count with multiple conditions then join

Quick one,
I have a table, with the following structure
id lid taken
1 1 0
1 1 0
1 1 1
1 1 1
1 2 1
Pretty simply so far right?
I need to query the taken/available from the lid of 1, which should return
taken available
2 2
I know I can simply do two counts and join them, but is there a more proficient way of doing this rather than two separate queries?
I was looking at the following type of format, but I can not for the life of me get it executed in SQL...
SELECT
COUNT(case taken=1) AS taken,
COUNT(case taken=0) AS available FROM table
WHERE
lid=1
Thank you SO much.
You can do this:
SELECT taken, COUNT(*) AS count
FROM table
WHERE lid = 1
GROUP BY taken
This will return two rows:
taken count
0 2
1 2
Each count corresponds to how many times that particular taken value was seen.
Your query is correct just needs juggling a bit:
SELECT
SUM(case taken WHEN 1 THEN 1 ELSE 0 END) AS taken,
SUM(case taken WHEN 1 THEN 0 ELSE 1 END) AS available FROM table
WHERE
lid=1
Alternatively you could do:
SELECT
SUM(taken) AS taken,
COUNT(id) - SUM(taken) AS available
FROM table
WHERE
lid=1
SELECT
SUM(case WHEN taken=1 THEN 1 ELSE 0 END) AS taken,
SUM(case WHEN taken=0 THEN 1 ELSE 0 END) AS available
FROM table
WHERE lid=1
Weird application of CTE's:
WITH lid AS (
SELECT DISTINCT lid FROM taken
)
, tak AS (
SELECT lid,taken , COUNT(*) AS cnt
FROM taken t0
GROUP BY lid,taken
)
SELECT l.lid
, COALESCE(a0.cnt, 0) AS available
, COALESCE(a1.cnt, 0) AS taken
FROM lid l
LEFT JOIN tak a0 ON a0.lid=l.lid AND a0.taken = 0
LEFT JOIN tak a1 ON a1.lid=l.lid AND a1.taken = 1
WHERE l.lid=1
;