Get a percentage of all in Access SQL - sql

I have a table containing a list of features that will be implemented by a given team for a given release, with a flag to tell me if the feature is testable or not.
Sample data can be:
feature team rel testable
1 1 1 1
2 1 1 1
3 1 1 1
4 1 2 1
5 1 2 1
6 1 2 0
7 1 3 0
8 1 3 0
9 1 3 1
10 2 1 0
11 2 1 0
12 2 1 0
13 2 2 1
14 2 2 0
15 2 2 0
16 2 3 1
17 2 3 1
18 2 3 0
What I try to get is, for each team and each release, what is the percentage of testable feature (over the overall count of features for this team and release.
Ideally I would like to keep it as a single SQL query due to the way I designed the display of the result.
I went as far as this:
SELECT
MyTable.team AS team,
MyTable.rel AS rel,
(COUNT(*)*100 / (
SELECT COUNT(*)
FROM MyTable
WHERE
[MyTable].team = team
AND [MyTable].rel = rel
)
) AS result
FROM MyTable
WHERE
MyTable.team IN (1,2)
AND MyTable.rel IN (1,2,3)
AND MyTable.testable = 1
GROUP BY
MyTable.rel,
MyTable.team
ORDER BY
MyTable.team,
MyTable.rel
Here is the result I expect (I don't really care about the rounding)
team rel result
1 1 1 // all are testable for team 1 release 1
1 2 0.66 // 2 out of 3 are testable for team 1 release 2
1 3 0.33
2 1 0
2 2 0.33
2 3 0.66
My feeling is that I am not that far from the solution, but I am not able to fix it.

I would think a simple average function would work here; assuming all values in the testable field are 1 or 0 only.
oh and get rid of testable = 1 in where clause
I'm not sure if access will implicitly cast the Boolean... so this will enable the avg to work by converting the value to 1,0 explicitly.
SELECT
MyTable.team AS team,
MyTable.rel AS rel,
AVG(iif(Testable,1,0)) AS result
FROM MyTable
WHERE
MyTable.team IN (1,2)
AND MyTable.rel IN (1,2,3)
GROUP BY
MyTable.rel,
MyTable.team
ORDER BY
MyTable.team,
MyTable.rel

select y.team, y.rel, x.cnt/y.tot as res
from (
select t.team, t.rel, sum(x.cnt) as tot
from (
select team, rel, testable, count(*) as cnt
from table where team in (1,2) and rel in (1,2,3)
group by team, rel, testable) x
join table t on t.team = x.team and t.rel = x.rel
group by team, rel) y
You can try this.

Related

Recursive query with CTE

I need some help with one query.
So, I already have CTE with the next data:
ApplicationID
CandidateId
JobId
Row
1
1
1
1
2
1
2
2
3
1
3
3
4
2
1
1
5
2
2
2
6
2
5
3
7
3
2
1
8
3
6
2
9
3
3
3
I need to find one job per candidate in a way, that this job was distinct for table.
I expect that next data from query (for each candidate select the first available jobid that's not taken by the previous candidate):
ApplicationID
CandidateId
JobId
Row
1
1
1
1
5
2
2
2
8
3
6
2
I have never worked with recursive queries in CTE, having read about them, to be honest, I don't fully understand how this can be applied in my case. I ask for help in this regard.
The following query returns the expected result.
WITH CTE AS
(
SELECT TOP 1 *,ROW_NUMBER() OVER(ORDER BY ApplicationID) N,
CONVERT(varchar(max), CONCAT(',',JobId,',')) Jobs
FROM ApplicationCandidateCTE
ORDER BY ApplicationID
UNION ALL
SELECT a.*,ROW_NUMBER() OVER(ORDER BY a.ApplicationID),
CONCAT(Jobs,a.JobId,',') Jobs
FROM ApplicationCandidateCTE a JOIN CTE b
ON a.ApplicationID > b.ApplicationID AND
a.CandidateId > b.CandidateId AND
CHARINDEX(CONCAT(',',a.JobId,','), b.Jobs)=0 AND
b.N = 1
)
SELECT * FROM CTE WHERE N = 1;
However, I have the following concerns:
The recursive CTE may extract too many rows.
The concatenated JobId may exceed varchar(max).
See dbfiddle.

SQL Query to get multiple resultant on single column

I have a table that looks something like this:
id name status
2 a 1
2 a 2
2 a 3
2 a 2
2 a 1
3 b 2
3 b 1
3 b 2
3 b 1
and the resultant i want is:
id name total count count(status3) count(status2) count(status1)
2 a 5 1 2 2
3 b 4 0 2 2
please help me get this result somehow, i can just get id, name or one of them at a time, don't know how to put a clause to get this table at once.
Here's a simple solution using group by and case when.
select id
,count(*) as 'total count'
,count(case status when 3 then 1 end) as 'count(status1)'
,count(case status when 2 then 1 end) as 'count(status3)'
,count(case status when 1 then 1 end) as 'count(status2)'
from t
group by id
id
total count
count(status3)
count(status2)
count(status1)
2
5
1
2
2
3
4
0
2
2
Fiddle
Here's a way to solve it using pivot.
select *
from (select status,id, count(*) over (partition by id) as "total count" from t) tmp
pivot (count(status) for status in ([1],[2],[3])) pvt
d
total count
1
2
3
3
4
2
2
0
2
5
2
2
1
Fiddle

SQL update order in relation ship based on ID

I wonder how to update order in this table for many-to-many relationship using SQL based on PostsId.
So my table now looks like:
I'm using SQL Server
BlogsId
PostsId
Order
1
1
1
0
2
2
1
0
3
3
2
0
3
4
2
0
3
5
3
0
3
6
3
0
but I want to update Order using SQL to this:
BlogsId
PostsId
Order
1
1
1
1
2
2
1
2
3
3
2
1
3
4
2
2
3
5
3
1
3
6
3
2
So for example: Blog with Id 3 is the first blog in Post with Id 2, Blog with Id 4 is the second Blog in Post with Id 2 and etc...
I've tried this:
DECLARE #myVar int
SET #myVar = 0
UPDATE [dbo].[BlogPost]
SET #myVar = [Order] = #myVar + 1
but then I got this:
BlogsId
PostsId
Order
1
1
1
1
2
2
1
2
3
3
2
3
3
4
2
4
3
5
3
5
3
6
3
6
So, I think I should do something in WHERE part (with Distinct maybe) but I don't know exactly what. I could write something in C# to do what I want but I don't know how to write something like this in SQL.
Physically maintaining an order or sequence of rows is rarely a good idea and can lead to data inconsistencies and other unforseen issues.
You would be better off creating a view that provides the additional Order column which you can do using row_number()
Create view BlogPosts as
select *,
Row_Number() over(partition by PostsId order by BlogsId) as [Order]
from blogpost;
If you really want to update an actual column in the table you could use a CTE
with b as (
select *,
Row_Number() over(partition by PostsId order by BlogsId) as seq
from blogpost
)
update b
set [Order] = seq;
You can update from a calculated row_number.
update t
set [Order] = rn
from (
select BlogsId, PostsId, [Order]
, rn = row_number() over (partition by PostsId order by BlogsId asc)
from BlogPost
) t
where ([Order] is null or [Order]!=rn);
select *
from BlogPost
order by BlogsId, PostsId
BlogsId
PostsId
Order
1
1
1
2
1
2
3
2
1
4
2
2
5
3
1
6
3
2
Demo on db<>fiddle here

finding records which doesnt have a state

AID
BID
STATE
1
1
1
1
2
3
1
3
3
2
1
0
2
2
3
2
3
3
3
1
3
3
2
0
3
3
3
I am trying to find AID records which doesnt have any 0 state in this example AID = 1 (will be multiple records)
If you would like to find all the AID with no 0 state records you may use
SELECT
AID
FROM
mytable
GROUP BY
AID
HAVING
COUNT(
CASE WHEN STATE=0 THEN 1 END
)=0;
AID
1
or if you would like to find all AID records where the state is not 0 for any AID record you may use the following.
SELECT
*
FROM
mytable
WHERE AID NOT IN (
SELECT AID FROM mytable WHERE STATE=0
)
AID
BID
STATE
1
1
1
1
2
3
1
3
3
Let me know if this works for you.

Best way to by column and aggregation on another column

I want to create a rank column using existing rank and binary columns. Suppose for example a table with ID, RISK, CONTACT, DATE. The existing rank is RISK, say 1,2,3,NULL, with 3 being the highest. The binary-valued is CONTACT with 0,1 or FAILURE/SUCESS. I want to create a new RANK that will order by RISK once a certain number of successful contacts has been exceeded.
For example, suppose the constraint is a minimum of 2 successful contacts. Then the rank should be created as follows in the two instances below:
Instance 1. Three ID, all have a min of two successful contacts. In that case the rank mirrors the risk:
ID risk contact date rank
1 3 S 1 3
1 3 S 2 3
1 3 F 3 3
1 3 F 4 3
2 2 S 1 2
2 2 S 2 2
2 2 F 3 2
2 2 F 4 2
3 1 S 1 1
3 1 S 2 1
3 1 S 3 1
Instance 2. Suppose ID=1 has only one successful contact. In that case it is relegated to the lowest rank, rank=1, while ID=2 gets the highest value, rank=3, and ID=3 maps to rank=2 because it satisfies the constraint but has a lower risk value than ID=2:
ID risk contact date rank
1 3 S 1 1
1 3 F 2 1
1 3 F 3 1
1 3 F 4 1
2 2 S 1 3
2 2 S 2 3
2 2 F 3 3
2 2 F 4 3
3 1 S 1 2
3 1 S 2 2
3 1 S 3 2
This is SQL, specifically Hive. Thanks in advance.
Edit - I think Gordon Linoff's code does it correctly. In the end, I used three interim tables. The code looks like that:
First,
--numerize risk, contact
select A.* ,
case when A.risk = 'H' then 3
when A.risk = 'M' then 2
when A.risk = 'L' then 1
when A.risk is NULL then NULL
when A.risk = 'NULL' then NULL
else -999 end as RISK_RANK,
case when A.contact = 'Successful' then 1
else NULL end as success
Second,
-- sum_successes_by_risk
select A.* ,
B.sum_successes_by_risk
from T as A
inner join
(select A.person, A.program, A.risk, sum(a.success) as sum_successes_by_risk
from T as A
group by A.person, A.program, A.risk
) as B
on A.program = B.program
and A.person = B.person
and A.risk = B.risk
Third,
--Create table that contains only max risk category
select A.* ,
B.max_risk_rank
from T as A
inner join
(select A.person, max(A.risk_rank) as max_risk_rank
from T as A
group by A.person
) as B
on A.person = B.person
and A.risk_rank = B.max_risk_rank
This is hard to follow, but I think you just want window functions:
select t.*,
(case when sum(case when contact = 'S' then 1 else 0 end) over (partition by id) >= 2
then risk
else 1
end) as new_risk
from t;