SQL query help: How to evaluate value/max(value) based on some conditions in same query - sql

Not an expert in SQL. I am using postgres database with EF migration. Stuck with this requirement. Here it goes.
My table is like this:
A B C D
20 1 1 1
59 0 0 1
57 1 1 1
10 1 0 0
30 1 1 1
15 0 0 0
The order of rows is like oldest to latest(top to bottom).
Half query I have from my project is as below:
SELECT dcr."A"
FROM "DCR" dcr
LEFT JOIN "DCM" dcm ON "Id" = dcm."DCRID"
LEFT JOIN "DC" dc ON dc."Id" = dcm."DCID"
WHERE dcr."B" != 0
AND dcr."C" != 0
AND dcr."B" != 0
ORDER BY "UtcDate" desc
limit(1)
This will fetch me the first part value of latest A when it matches condition. But not the Max part and the division part as explained below.
I want to find the result of ((latest A where B = C = D = 1 divided by max of A in its previous rows where B = C = D = 1) - 1) * 100.
I want this to happen in single query and there are multiple groups like this. Lets say the table contains around 60 rows and we can group them based on some other column. Each group should evaluate this above formula.
Expected result for above example should be:
result = ((30 / 57) - 1) * 100 = (0.5263 - 1) * 100 = -47.73

You can use a subquery to get the max. I don't know why you're writing the query in that strange style, but I will keep it:
SELECT dcr."A" / (SELECT MAX("A")
FROM "DCR"
WHERE dcr."B" != 0
AND dcr."C" != 0
AND dcr."D" != 0)) - 1) * 100
FROM "DCR" dcr
LEFT JOIN "DCM" dcm ON "Id" = dcm."DCRID"
LEFT JOIN "DC" dc ON dc."Id" = dcm."DCID"
WHERE dcr."B" != 0
AND dcr."C" != 0
AND dcr."D" != 0
ORDER BY "UtcDate" desc
limit(1)

maybe something like this?
select (t1."A"/max(t2."A"))*100 from
(select row_number() over() as id,*
from t
where t."A"=1 and t."B" =1 and t."C"=1 ) as t1
join
(select row_number() over() as id,*
from t
where t."A"=1 and t."B" =1 and t."C"=1 ) as t2
on t1.id>t2.id
group by t1."A",t1."E"

Related

Add custom bool column where data is calculated based on values from linked entities

I have 2 tables: Entity and EntityItem.
EntityItems table has a Reason column which is nullable enum.
I'm trying to write a view that would return some Entititys columns and additionally a boolean column that states whether all corresponding EntityItem.Reason have a non-null value.
The following query returns somewhat what I want:
SELECT EntityItem.Id, COUNT(EntityItem.Reason) As Test
FROM EntityItem
GROUP BY EntityItem.ParentEntityId
ORDER BY Test DESC
Example output:
Id Test
132189 4
132190 2
132197 1
1 0
2 0
3 0
4 0
5 0
6 0
However, when I try to add this to a final query I get duplicated lines for each EntityItem
SELECT [Entity].[Id],
...
(SELECT CASE WHEN (SELECT COUNT([EntityItem].[Reason]) FROM [EntityItem] WHERE [EntityItem].[ParentEntityId] = [Entity].[Id]) = 0
THEN 0
ELSE 1
END) AS Test
FROM [Entity]
...
LEFT JOIN [EntityItem] ON [Entity].[Id] = [EntityItem].[ParentEntityId]
Example output:
Id Test
1 1
1 1
2 0
2 0
2 0
2 0
3 1
3 1
4 0
Question 1: Is my approach correct?
Question 2: Is there a way to remove duplicated lines without DISTINCT?
For your second query you need to aggregate before joining, for example by using outer apply something like:
select e.Id,
case when i.cnt = 0 then 0 else 1 end as Test
from Entity e
outer apply (
select Count(Reason) cnt
from EntityItem i
where i.ParentEntityId = e.Id
)i;
Saying that, since you are always returning a value of 1 if the count is greater than zero you don't actually need to count anything:
select e.Id,
case when exists (
select * from EntityItem i
where i.ParentEntityId = e.Id
)
then 1 else 0 end as Test
from Entity e;

How to Select * FROM table1, table2 correctly when table1 = table2

There is a table cs_goods_features like this
(id - product id, f - product property name, v - property value)
id
f
v
1
f1
10
1
f2
15
1
f3
25
2
f1
10
2
f2
15
2
f3
35
3
f1
15
3
f2
10
3
f3
55
I need to select only those products id, which have, for example, f1 = 10 and f2 = 15.
If I make a query like this
SELECT id
FROM cs_goods_features
where (f in ('f1', 'f2'))
and (v in (10,15))
then everything is fine except when the table has opposite values - not f1=10 and f2=15, but f1=15 and f2=10. I don't need such lines in result set.
What I need can be done like this:
select g1.id, g2.id
FROM cs_goods_features g1, cs_goods_features g2
WHERE g1.f = 'f1'
and g1.v = 10
and g2.f = 'f2'
and g2.v = 15
But the problem here is that I get two columns in the output (and if i need to select products by 3 properties - there would be 3 columns). And I'm not satisfied with it, because actually this query is going to be a subquery in a larger query.
In other words there will be
SELECT *
FROM tablename
where ID in (our_query)
It means I need exactly one column with results for construction "where ID in (...)" to work correctly
One way to do it is by counting for each id the number of times (f1, 10) and (f2, 15) appear and selecting distinct id where they appear at least once each:
select distinct id from
(select *,
sum(case when f = 'f1' and v = 10 then 1 else 0 end) over(partition by id) as f1_10,
sum(case when f = 'f2' and v = 15 then 1 else 0 end) over(partition by id) as f2_15
from cs_goods_features) t
where f1_10 > 0 and f2_15 > 0
Fiddle
I think you just separate into 2 groups like below:
SELECT id
FROM cs_goods_features
WHERE (f = 'f1' AND v = 10)
OR (f = 'f2' AND v = 15)

I just started learning SQL and I couldn't do the query, can you help me?

There is a field in the sql query that I can't do. First of all, a new column must be added to the table below. The value of this column needs to be percent complete, so it's a percentage value. So for example, there are 7 values from Cupboard=1 shelves. Where IsCounted is here, 3 of them are counted. In other words, those with Cupboard = 1 should write the percentage value of 3/7 as the value in the new column to be created. If the IsCounted of the others is 0, it will write zero percent. How can I do this?
My Sql Code:
SELECT a.RegionName,
a.Cupboard,
a.Shelf,
(CASE WHEN ToplamSayım > 0 THEN 1 ELSE 0 END) AS IsCounted
FROM (SELECT p.RegionName,
r.Shelf,
r.Cupboard,
(SELECT COUNT(*)
FROM FAZIKI.dbo.PM_ProductCountingNew
WHERE RegionCupboardShelfTypeId = r.Id) AS ToplamSayım
FROM FAZIKI.dbo.DF_PMRegionType p
JOIN FAZIKI.dbo.DF_PMRegionCupboardShelfType r ON p.Id = r.RegionTypeId
WHERE p.WarehouseId = 45) a
ORDER BY a.RegionName;
The result is as in the picture below:
It looks like a windowed AVG should do the trick, although it's not entirely clear what the partitioning column should be.
The SELECT COUNT can be simplified to an EXISTS
SELECT a.RegionName,
a.Cupboard,
a.Shelf,
a.IsCounted,
AVG(a.IsCounted * 1.0) OVER (PARTITION BY a.RegionName, a.Cupboard) Percentage
FROM (
SELECT p.RegionName,
r.Shelf,
r.Cupboard,
CASE WHEN EXISTS (SELECT 1
FROM FAZIKI.dbo.PM_ProductCountingNew pcn
WHERE pcn.RegionCupboardShelfTypeId = r.Id
) THEN 1 ELSE 0 END AS IsCounted
FROM FAZIKI.dbo.DF_PMRegionType p
JOIN FAZIKI.dbo.DF_PMRegionCupboardShelfType r ON p.Id = r.RegionTypeId
WHERE p.WarehouseId = 45
) a
ORDER BY a.RegionName;

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)

Join Two Select Statements With Different Columns And Condition

I'm not good at asking question, so i'll give an example of what i want to have.
if i = 1 and xi = 0 then
select a,b,c,d,e,f,g where z = 1
elseif i=0 and xi = 1 then
select a,c,f,h,l,n where w = var
elseif i=1 and xi=1 then
select a,b,c,d,e,f,g, where z = 1
union all
select a,c,f,h,l,n where w = var
end if
How can I join the 2 select statement if their columns are not equal and they both have a unique condition?
Based on the conditions you can create derived tables to fetch desired columns and then to get a union of the two tables add null values in column list of derived tables which have less number of columns:
Pseudo code:
select * from
(select a,b,c,d,e,f,g
where z = 1
and 1 = case when i = 1 and xi = 0 then 1
when i = 1 and xi = 1 then 1
else 0
end) as T1
union all
(select a,c,f,h,l,n ,null -- add null value to equate number of columns
where w = var
and 1 = case when i=0 and xi = 1 then 1
when i=1 and xi = 1 then 1
else 0
end) as T2
Hope this helps!!!
If it is not a requirement not to use dynamic sql I will opt for that one.
Another idea will be to use user defined function returnin tables.
So you encapsulate there the logic...