How to use min() together with other column in SQL - sql

I am building an application consisting of an SQL backend and several microservices that communicate with this backend. I am trying to get as little data as possible from the database over the wire so I am trying to fetch the exact records so I don't have to start looping in the application logic.
So this is the source table :
OrderID
Topic
hierarchyLevel
Responsible Person
Status
1234
A
0
Jason
Open
1234
A
0
Carol
Open
1234
A
1
Jeff
Open
1234
A
2
Alina
Open
1234
A
3
Eileen
Open
1234
B
0
John
Closed
1234
B
1
Danny
Open
1234
B
1
Celine
Open
1234
B
2
Corry
Open
1234
B
3
Logan
Open
1234
C
0
Jason
Closed
1234
C
1
Annisa
Open
1234
C
2
Cedric
Open
1234
D
0
Peter
Closed
1234
D
1
Joanna
Closed
1234
D
2
Freeke
Open
1234
E
0
Carol
Closed
1234
E
1
Cynthia
Closed
1234
E
2
Deidra
Open
Based on the "orderID" as input parameter for the query , I should get the for every topic the next persons in line , so with the lowest HierarchyLevel number for that topic, that has status "Open". So every topic can return multiple times as long as the returned records have the lowest possible value in "HierarchyLevel" and the status is "Open".
So I would expect this as output for the stored procedure :
OrderID
Topic
hierarchyLevel
Responsible Person
Status
1234
A
0
Jason
Open
1234
A
0
Carol
Open
1234
B
1
Danny
Open
1234
B
1
Celine
Open
1234
C
1
Annisa
Open
1234
D
2
Freeke
Open
1234
E
2
Deidra
Open
I tried to work with min() but with no luck:
Some things I tried :
select * from mytable as a
inner join (
select Topic, min(HierarchyLevel) as min_value
from mytable
group by Topic
) t on t.Topic = a.Topic and a.HierarchyLevel = min_value and a.OrderID = #OrderID and Status = 'Open'
select * from mytable as a
inner join (
select Topic, Status ,min(HierarchyLevel) as min_value
from mytable
group by Topic , Status HAVING Status = 'Open'
) t on t.Topic = a.Topic and a.HierarchyLevel = min_value and a.OrderID = #OrderID and a.Status = 'Open'
None with the desired result. Could anyone guide me in the right direction?
Thank you very much.

Lookup RANK() OVER (...). I guess something like:
SELECT OrderID, Topic, hierarchyLevel, Responsible_Person, Status
FROM (
SELECT OrderID, Topic, hierarchyLevel, Responsible_Person, Status
, RANK() OVER (PARTITION BY OrderID, Topic
ORDER BY hierarchyLevel) AS rnk
FROM t
WHERE Status = 'Open'
) AS T
WHERE rnk = 1;

One straight-forward solution is to use a correlated aggregate using exists:
select *
from t
where exists (
select * from t t2
where t2.OrderId = t.OrderId
and t2.Topic = t.Topic
and t2.Status = 'Open'
group by t2.OrdeRId, t2.Topic
having Min(t2.hierarchyLevel) = t.hierarchyLevel
);
Demo DB<>Fiddle
Result:

One way is to check if all the records with open status intersect with said records also having the lowest heirarchy_level per order_id and topic
select order_id, topic, heirarchy_level, responsible_person, status
from t
where status='Open'
intersect
select order_id, topic, min(heirarchy_level) over (partition by order_id, topic), responsible_person, status
from t
where status='Open'

Related

Find rows in which a column value only occurs once (Single-Sided Entries in Double Entry accounting system)

I have a table of bank transactions, AccountTransaction, and rows with for e.g.
Amount
Payee_Name
Transaction_ID
Is_Corresponding_Transaction
69.00
Bob Jones
1
1
-69.00
Bob Jones
1
0
25.00
Bill
2
1
-25.00
Bill
2
0
297.00
Sally
3
1
-5.00
Ted
4
1
2.50
Ted
4
0
2.50
Ted
4
0
How do I select only (all) TS like Sally's where the Transaction ID only occurs once?
Bonus points: How do I select TS like Ted's where the sum of all Is_Corresponding_Transaction = 0 != the sum of Is_Corresponding_Transaction = 1 for a given TS_ID?
I was looking and found a Group by or where not exists, but couldn't figure out how to get that to work
Here's an e.g. of what I tried:
select
Full_Name, amount, a.Posted_Date,a.Payee_Name, a.Memo, Accounts.Account_Name
from AccountTransaction a
left join Accounts on Accounts.Account_Code = a.Account_Code
left join users on a.UserId = users.UserId
where not exists (select 1 from AccountTransaction b where a.Transaction_ID = b.Transaction_ID having count(*)>1)
and a.Pending= 0
ORDER by a.Posted_Date desc
Just to expand on Stu's comment. Here is one option that uses the window function
with cte as (
Select *
,NetSum = sum(Amount) over (partition by Transaction_ID)
,NetCnt = sum(1) over (partition by Transaction_ID)
From YourTable
)
Select *
From cte
Where NetSum<>0
or NetCnt<>2

SQL help i need to find the inventory remaining in my office

In sql help i have 3 tables, table one is asset table which is as follow
id
asset_code
asset_name
asset_group
asset_quantity
1
A001
demo asset
4
5
2
A002
demo asset 2
6
3
and another table is asset_allocation
id
asset_id
allocated_quantity
allocated_location
1
1
2
IT office
2
1
1
main hall
the last table is asset_liquidated which will present assets that are no longer going to be used
id
asset_id
liquidated_quantity
1
1
2
2
1
1
lets say i have 5 computers and i have allocated 3 computers and 1 is no longer going to be used so i should be remaining with 1 computer so now how do i make sql auto generate this math for me
You need to use aggregation and the join your tables -
SELECT id, asset_code, asset_name, asset_group, asset_quantity,
asset_quantity - COALESCE(AA.allocated_quantity, 0) - COALESCE(AL.liquidated_quantity, 0) available_quantity
FROM asset A
LEFT JOIN (SELECT asset_id, SUM(allocated_quantity) allocated_quantity
FROM asset_allocation
GROUP BY asset_id) AA ON A.id = AA.asset_id
LEFT JOIN (SELECT asset_id, SUM(liquidated_quantity) liquidated_quantity
FROM asset_liquidated
GROUP BY asset_id) AL ON A.id = AL.asset_id
This query will give you -1 as available_quantity for asset_id 1 as you have only 5 available, 3 of them are allotted and 3 are liquidated as per your sample data.
Please see if this helps
SELECT
asset_quantity AS Total_Assets
,ISNULL(allocated_quantity, 0) allocated_quantity
,ISNULL(liquidated_quantity, 0) liquidated_quantity
FROM asset
LEFT OUTER JOIN (
SELECT
asset_id, SUM(allocated_quantity) AS allocated_quantity
FROM asset_allocation
GROUP BY asset_id
) asset_allocation2
ON asset_allocation2.asset_id = asset.id
LEFT OUTER JOIN (
SELECT
asset_id, SUM(liquidated_quantity) AS liquidated_quantity
FROM asset_liquidated
GROUP BY asset_id
) asset_liquidated 2
ON asset_liquidated 2.asset_id = asset.id

CASE expression sum - issue when joining to other tables

I have a case expression in a stored procedure summing an account field, and then inserting into a user id. The logic works... until joining to another table.
I tried adding distinct counts, and additional tables to the query, but still when I join to another table it applies the 1 value when I want it to be 0 to the account.
This is the calculation in the stored proc;
INSERT INTO #SUMMARY_TEMP (USER_ID,FSN_CNT )
(SELECT USER_ID,
SUM(CASE WHEN A_GROUP_CD = 'RED' AND A_TYPE_CD = 'FSN' THEN REC_COUNT ELSE 0 END)
) AS 'FSN_CNT',
FROM (SELECT A_ACCOUNT_NBR,
A_USER_ID,
A_GROUP_CD,
A_TYPE_CD,
COUNT(*) AS REC_COUNT
FROM EXCEPTION_DETAIL
INNER JOIN #STAFF ON A_REPORT_DT = #REPORT_DT
AND (A_USER = B_USER_ID)
GROUP BY A_ACCOUNT_NBR,
A_USER_ID_ID,
A_GROUP_CD,
A_TYPE_CD) EXCEPTIONS
GROUP BY A_USER_ID,
A_ACCOUNT_NBR)
This is the result which is what I expect for 2 USER Ids
A_ACCOUNT_NBR USER_ID FSN_CNT
123456 HENRY 0
123498 HENRY 1
374933 JOE 1
474930 JOE 0
but when I join to another table the data looks like
A_ACCOUNT_NBR USER_ID FSN_CNT
123456 HENRY 1
123498 HENRY 1
374933 JOE 1
474930 JOE 1
Its applying the 1 value to account 123456 & 474930 when it should be 0.
I think its because the other table does not have the ACCOUNT_NBR column - I am joining on USER_ID and so it applies the 1 to all ACCOUNT_NBR from table A.
Thanks for all the suggestions, I tried using a CTE, and the counts now look good, but its created duplicate rows as shown below. Any suggestions on how to remove the duplication, below is the join I am using for the CTE;
select cte.*, jt.USER_ID
from cte
join EXCEPTION_DETAIL jt on cte.USER_ID=jt.USER_ID
USER ACCOUNT_NBR FSN_CNT
HENRY 123456 0
HENRY 123456 0
HENRY 123498 1
HENRY 123498 1
JOE 374933 1
JOE 374933 1
JOE 474930 0
JOE 474930 0
you can separate the 1st query by using cte and join with it next level like below
with cte as
(
(SELECT USER_ID,
SUM(CASE WHEN A_GROUP_CD = 'RED' AND A_TYPE_CD = 'FSN' THEN REC_COUNT ELSE 0 END)
) AS 'FSN_CNT',
FROM (SELECT A_ACCOUNT_NBR,
A_USER_ID,
A_GROUP_CD,
A_TYPE_CD,
COUNT(*) AS REC_COUNT
FROM EXCEPTION_DETAIL
INNER JOIN #STAFF ON A_REPORT_DT = #REPORT_DT
AND (A_USER = B_USER_ID)
GROUP BY A_ACCOUNT_NBR,
A_USER_ID_ID,
A_GROUP_CD,
A_TYPE_CD) EXCEPTIONS
GROUP BY A_USER_ID,
A_ACCOUNT_NBR)
) select cte.*,jt.USER_ID from cte join jointable_name jt on cte.USER_ID=jt.USER_ID

Last Record Of Each Group Using Multiple Joins In Sql Server

I Have 3 tables Customer, Bank and BankTransaction.
On My View I want to display each customer with their Balance in the bank account.
Here is my tables
Customer
Id Name
---------
1 John
2 Jack
Bank
Id CustomerId BankName
----------------------------------
1 1 HSBC
2 2 HSBC
BankTransaction
Id BankID MoneyIn MoneyOut Balance
---------------------------------------------
1 1 1000 0 1000
2 1 0 500 500
3 2 2000 0 2000
4 2 2000 0 4000
5 2 1000 0 5000
Now I want to Display following data view query
John 500
Jack 5000
Last Balance of each customer
Try this
SELECT A.Name,
Sum(C.MoneyIN) - Sum(C.MoneyOut) AS Balance
FROM #Customer A
JOIN #Bank B ON A.Id = B.id
JOIN #BankTransaction C ON B.Id = C.BankID
GROUP BY A.Name
Using Window function you can get the result. Try this.
;WITH cte
AS (SELECT Row_number() OVER (partition BY b.id ORDER BY a.id DESC) rn,
c.Name,
a.balance
FROM BankTransaction a
JOIN bank b ON a.BankID = b.Id
JOIN Customer c ON c.Id = b.CustomerId)
SELECT name, balance
FROM cte
WHERE rn = 1

Tricky SQL - Select non-adjacent numbers

Given this data on SQL Server 2005:
SectionID Name
1 Dan
2 Dan
4 Dan
5 Dan
2 Tom
7 Tom
9 Tom
10 Tom
How would I select records where the sectionID must be +-2 or more from another section for the same name.
The result would be:
1 Dan
4 Dan
2 Tom
7 Tom
9 Tom
Thanks for reading!
SELECT *
FROM mytable a
WHERE NOT EXISTS
(SELECT *
FROM mytable b
WHERE a.Name = b.Name
AND a.SectionID = b.SectionID + 1)
Here's LEFT JOIN variant of Anthony's answer (removes consecutive id's from the results)
SELECT a.*
FROM mytable a
LEFT JOIN mytable b ON a.Name = b.Name AND a.SectionID = b.SectionID + 1
WHERE b.SectionID IS NULL
EDIT: Since there is another interpretation of the question (simply getting results where id's are more than 1 number apart) here is another attempt at an answer:
WITH alternate AS (
SELECT sectionid,
name,
EXISTS(SELECT a.sectionid
FROM mytable b
WHERE a.name = b.name AND
(a.sectionid = b.sectionid-1 or a.sectionid = b.sectionid+1)) as has_neighbour,
row_number() OVER (PARTITION by a.name ORDER BY a.name, a.sectionid) as row_no
FROM mytable a
)
SELECT sectionid, name
FROM alternate
WHERE row_no % 2 = 1 OR NOT(has_neighbour)
ORDER BY name, sectionid;
gives:
sectionid | name
-----------+------
1 | Dan
4 | Dan
2 | Tom
7 | Tom
9 | Tom
Logic: if a record has neighbors with same name and id+/-1 then every odd row is taken, if it has no such neighbors then it gets the row regardless if it is even or odd.
As stated in the comment the condition is ambiguous - on start of each new sequence you might start with odd or even rows and the criteria will still be satisfied with different results (even with different number of results).