Sorting in SQL w/ multiple conditions - sql

I'm new to SQL, how would I go along with sorting the following:
I have three different type of dev projects(type 1,2, and 3). Type 1 and 2 also have an analysis that is linked with them. The analysis and dev type 1 and 2 will have a linking parent id, how would I sort with these specs:
Process Items in the following sequence
Dev type 3 projects
Dev type 1 or 2 projects combined with their analysis item (dev item columns take precedence analysis item columns)
Remaining analysis projects with no related dev projects
*EDIT* the lower ParentID should come first after these three specs
Example columns of the table are:
|ParentID| AnalysisItemID | DevItemId | DevType |
1 Null 2 1
4 5 Null Null
6 8 Null Null
8 Null 9 3
6 Null 7 2
1 3 Null Null
Sorted Output would be:
|ParentID| AnalysisItemID | DevItemId | DevType |
8 Null 9 3
1 Null 2 1
1 3 Null Null
6 Null 7 2
6 8 Null Null
4 5 Null Null

To me, it looks like you want to sort the data by the parent id, with the ordering based on the maximum devtype for the parent.
For this, you need to join to a summary table to get the information for the order by:
select t.*
from t join
(select parentId, max(devtype) as maxdt
from t
group by parentId
) p
on t.parentId = p.parentId
order by (case when maxdt = 3 then 1
when maxdt in (1, 2) then 2
else 3
end), parentId, devtype desc

It's ugly, but you can order by the result of a CASE in SQL. I imagine something similar to:
SELECT * FROM MyTable
ORDER BY CASE WHEN DevType = 3 THEN 1
WHEN DevType IN (1, 2) THEN AnalysisItemID + 1
ELSE [bignumber] + AnalysisItemID END
could do what you require.
Obviously, that case can also be encapsulated into a user-defined function for readability if required. There's a chance that that could affect performance, however.

Related

Identify a FK which has the highest value from a list of values in its source table

I have following tables.
Part
id
name
1
Part 1
2
Part 2
3
Part 3
Operation
id
name
part_id
order
1
Op 1
1
10
2
Op 2
1
20
3
Op 3
1
30
4
Op 1
2
10
5
Op 2
2
20
6
Op 1
3
10
Lot
id
part_id
Operation_id
10
1
2
11
2
5
12
3
6
I am selecting the results from Lot table and I want to select a column last_Op which is based on the order value of the operation_id. If value of order for the operation_id is the highest for the respective part_id, return 1 else return 0
SELECT
id,
part_id,
operation_id,
last_Op
FROM Lot
expected result set based on the tables above.
id
part_id
operation_id
last_op
10
1
2
0
11
2
5
1
12
3
6
1
In above example, first row returns last_op = 0 because operation_id = 2 is associated with part_id = 1 and it has the highest order = 30. Since operation_id for this part is not pointing towards the highest order value, 0 is returned.
The other two rows return 1 because operation_id 5 and 6 are associated with part_id 2 and 3 respectively and they are pointing towards the highest 'order' value.
If value of order for the operation_id is the highest for the respective part_id, return 1 else return 0
This sounds like window functions will help:
select l.*,
(case when o.order = o.max_order then 1 else 0 end) as last_op
from lot l left join
(select o.*,
max(o.order) over (partition by o.part_id) as max_order
from operations o
) o
on l.operation_id = o.id;
Note: order is a very poor name for a column because it is a SQL keyword.

Custom aliases for all fields with GROUP BY ROLLUP

I have such tables:
Group - combination of TypeId and ZoneId
ID TypeID ZoneID
-- -- --
1 1 1
2 1 2
3 2 1
4 2 2
5 2 3
6 3 3
Object
ID GroupId
-- --
1 1
2 1
3 2
4 3
5 3
6 3
I want to build a query for grouping all these tables by TypeId and ZoneId, with number of objects which have specific combination of these field:
ResultTable
TypeId ZoneId Number of objects
-- -- --
1 1 2
1 2 1
2 1 3
2 2 1
2 3 0
3 3 0
Query for this:
SELECT
group.TypeId,
group.ZoneId,
COUNT(obj.ID) as NumberOfObjects
FROM[Group] group
JOIN[Object] obj on obj.GroupID = group.ID
GROUP BY group.TypeId, group.ZoneId ORDER BY group.TypeId
But! I want to add summarize row after each group, and make it like:
ResultTableWithSummary
TypeId ZoneId Number of objects
-- -- --
1 1 2
1 2 1
Summary (empty field) 3
2 1 3
2 2 1
2 3 0
Summary (empty field) 4
3 3 0
Summary (empty field) 0
The problem is that I can use GROUP BY ROLLUP(group.TypeId, group.ZoneId):
TypeId ZoneId Number of objects
-- -- --
1 1 2
1 2 1
1 null 3
2 1 3
2 2 1
2 3 0
2 null 4
3 3 0
3 null 0
but I cannot or don't know how to change not-null group.TypeId in summary rows with "Summary".
How can I do this?
The simplest method is coalesce(), but you need to be sure the types match:
SELECT COALESCE(CONVERT(VARCHAR(255), group.TypeId, 'Summary') as TypeId,
. . .
This is not the most general method, because it does not handle real NULL values in the GROUP BY keys. That doesn't seem to be an issue in this case. If it were, you could use a CASE expression with GROUPING().
EDIT:
For your particular variant (which I find strange), you can use:
SELECT (CASE WHEN group.TypeId IS NULL OR group.ZoneID IS NULL
THEN 'Summary' ELSE CONVERT(VARCHAR(255), group.TypeId)
END) as TypeId,
. . .
In practice, I would use something similar to the COALESCE() in both columns, so I don't lose the information on what the summary is for.

How to use a new column as a flag to show Null row?

I have table A:
id
1
2
3
4
5
and table B:
id
2
3
4
I left join A and B:
id id
1 NULL
2 2
3 3
4 4
5 NULL
And how can I get a new column like this:
id id flag
1 NULL 0
2 2 1
3 3 1
4 4 1
5 NULL 0
Generally speaking, I want all rows in A but not in B to be flaged as 0 and want all rows in both tables to be flaged as 1. How can I achieve that? Better not use CTE.
This is just a CASE expression:
CASE WHEN B.id IS NULL THEN 0 ELSE 1 END AS flag
Alternatively, you could use an IIF (which is shorthand CASE expression):
IIF(b.id IS NULL, 0,1)
I would recommend using exists:
select a.*,
(case when exists (select 1 from b where b.id = a.id
then 1 else 0
end) as flag
from a;
The purpose of using exists instead of left join is that you are guaranteed to not get duplicate rows -- even if ids are duplicated in b. That is a nice guarantee.
From a performance perspective, the two should be similar, but it is possible that the case is an iota faster.

Fetching data from DB and populate a partitioned List

I am confused about this both from front end point of view as well as querying the data from SQLite Database. If you have any idea how to solve either of these please do answer.
SQLite Database
I have a table likes this:
transactionId | productId | quantity
1 2 1
2 4 0
3 1 null
4 3 1
5 9 1
6 6 0
7 1 1
8 7 1
9 8 1
10 2 1
11 0 null
12 3 1
13 5 1
14 7 1
15 1 0
16 2 1
17 9 1
18 0 null
19 2 1
Now I want to display this data in groups of 5 units(i.e. groups till 5 units are completed) in list in my flutter app.
So 1st group will have 8 items,
2nd will have 6 items,
and 3rd group will have 5 items
(and is still incomplete since more items can be added till quantity for that group becomes 5)
Something like this:
Now my App can have multiple groups like this. Also, I don't think Grid view builder can work here since for each group I'll have to display some data for the group as well as accumulated data (which isn't shown in the picture)
Questions:
1) How to query data from SQFLite database?
2) How to display the queried data in my Flutter App front end?
Unfortunately, this type of problem requires a recursive CTE (or other iterative processing).
Assuming that transactionId is consecutive with no gaps:
with recursive cte as (
select transactionId, productId,
coalesce(quantity, 0) as quantity,
1 as bin
from t
where transactionId = 1
union all
select t.transactionId, t.productId,
(case when cte.quantity > 5
then 0 else cte.quantity
end) + coalesce(t.quantity, 0) as quantity,
(case when cte.quantity > 5 then 1 else 0 end) + cte.bin as bin
from cte join
t
on t.transactionId = cte.transactionId + 1
)
select *
from cte;
If transactionId has gaps or other issues, just use row_number() (in another CTE) to create an appropriate column for the where clauses.

Add auto incrementing number based on column

I am trying to wrap my head around a problem I hit exporting data from one system to another.
Let's say I have a table like:
id | item_num
1 1
2 1
3 2
4 3
5 3
6 3
I need to add a column to the table and update it to contain an incrementing product_num field based on item. This would be the end result given the above table.
id | item_num | product_num
1 1 1
2 1 2
3 2 1
4 3 1
5 3 2
6 3 3
Any ideas on going about this?
Edit: This is being done in Access 2010 from one system to another (sql server source, custom/unknown ODBC driven destination)
Perhaps you could create a view in your SQL Server database and then select from that in Access to insert into your destination.
Possible solutions in SQL Server:
-- Use row_number() to get product_num in SQL Server 2005+:
select id
, item_num
, row_number() over (partition by item_num order by id) as product_num
from MyTable;
-- Use a correlated subquery to get product_num in many databases:
select t.id
, t.item_num
, (select count(*) from MyTable where item_num = t.item_num and id <= t.id) as product_num
from MyTable t;
Same result:
id item_num product_num
----------- ----------- --------------------
1 1 1
2 1 2
3 2 1
4 3 1
5 3 2
6 3 3