Select As Status If Subtable Lines Include Value - sql

I have 2 Table named Order and OrderDetails
Order Table
orderID orderDate OrderUserId
1 10 01.01.2010 .....
2 20 05.05.2011 .....
OrderDetails Table
DetailID OrderIdOfDetail DetailProductID DetailStatusID
1 25 10 xxx 1
2 26 10 xxx 2
3 27 10 xxx 2
4 28 10 xxx 0
5 29 10 xxx 1
6 30 20 xxx 1
7 31 20 xxx 2
8 32 20 xxx 0
DetailStatusId for closed , pending, cancelled bla...
For example I want to select orderID : 10.
If detail lines include DetailsStatusID 1 select order Status as "Pending".
If detail lines include DetailsStatusID 2 select order Status as "Open".
If all DetailsStatusID is equal and 0 select order status as "Closed"
I tried "if exist" but couldn't success.
How can I do it ?

You can use a query like the following:
SELECT o.orderID,
CASE
WHEN MAX(CASE WHEN DetailStatusID = 1 THEN 1 ELSE 0 END) +
MAX(CASE WHEN DetailStatusID = 2 THEN 1 ELSE 0 END) +
MAX(CASE WHEN DetailStatusID = 0 THEN 1 ELSE 0 END) >= 2
THEN 'MultiStatus'
WHEN COUNT(CASE WHEN DetailStatusID = 1 THEN 1 END) > 0 THEN 'Pending'
WHEN COUNT(CASE WHEN DetailStatusID = 2 THEN 1 END) > 0 THEN 'Open'
WHEN MAX(DetailStatusID) = 0 THEN 'Closed'
END AS Status
FROM Order AS o
JOIN OrderDetails AS od ON o.orderID = od.OrderIdOfDetail
GROUP BY orderID
The query uses a CASE expression in order to calculate the Status field:
It first checks whether the order contains at least one record with DetailStatusID = 1. If it does 'Pending' is returned.
It then checks whether the order contains at least one record with DetailStatusID = 2. If it does 'Open' is returned.
It then checks whether all records of the group have DetailStatusID = 0. In this case 'Closed' is returned. I assume that DetailStatusID cannot take negative vaues.

This uses a left join to an derived table (subquery) that uses conditional aggregation to count the different DetailStatusId. This will also return a result of Other for Status if, for some reason, the OrderId has no corresponding details record.
select o.*
, [Status]=case
when od.Status0 > 0 and od.Status0 = od.DetailCount
then 'Closed'
when od.Status1 > 0
then 'Pending'
when od.Status2 > 0
then 'Open'
else 'Other'
end
from orders as o
left join (
select
OrderIdOfDetail
, DetailCount = count(*)
, Status0 = sum(case when DetailStatusId = 0 then 1 else 0 end)
, Status1 = sum(case when DetailStatusId = 1 then 1 else 0 end)
, Status2 = sum(case when DetailStatusId = 2 then 1 else 0 end)
from OrderDetails
group by OrderIdOfDetail
) as od
on o.OrderId = od.OrderIdOfDetail
rextester demo: http://rextester.com/YPA79670
query results:
+---------+---------------------+-------------+---------+
| orderID | orderDate | OrderUserId | Status |
+---------+---------------------+-------------+---------+
| 10 | 01.01.2010 00:00:00 | 1 | Pending |
| 20 | 05.05.2011 00:00:00 | 2 | Pending |
+---------+---------------------+-------------+---------+
subquery only results:
+-----------------+-------------+---------+---------+---------+
| OrderIdOfDetail | DetailCount | Status0 | Status1 | Status2 |
+-----------------+-------------+---------+---------+---------+
| 10 | 5 | 1 | 2 | 2 |
| 20 | 3 | 1 | 1 | 1 |
+-----------------+-------------+---------+---------+---------+

select *, case when DetailStatusID = '1' then 'Pending'
when DetailStatusID = '2' then 'Open'
when DetailStatusID = '0' then 'Closed'
end as OrderStatusDescription
From OrderDetails
Where OrderIdOfDetail = '10'

Related

How to query all months from January to December

How to query all months from January to December for all values. The values of 0 should also be presented in query result.
My query:
SELECT *
FROM
(SELECT
MONTH(begin_ts) AS [Month],
SUM(CASE
WHEN bmktonr = '7'
THEN dauer
ELSE reserve1
END) + SUM(CASE
WHEN bmktonr = '11'
THEN dauer
ELSE reserve1
END) AS Prozess_Verfügbarkeit,
SUM(CASE
WHEN bmktonr = '1'
THEN dauer
ELSE reserve1
END) + SUM(CASE
WHEN bmktonr = '2'
THEN dauer
ELSE reserve1
END) AS Verfügbarkeit
FROM
[hydra1].[hydadm].[v_ereignis]
WHERE
masch_nr = 'FIMI1'
AND YEAR(begin_ts) = YEAR(CURRENT_TIMESTAMP)
GROUP BY
MONTH(begin_ts)) T
INNER JOIN
(SELECT
p.masch_nr,
SUM(b.ruest_zeit) AS SOLLRüsten,
SUM(b.bearb_zeit) AS SOLLProduktion,
SUM(b.ruest_zeit_zuschl) AS SOLLZuschlag,
SUM(p.bmk_07) AS ISTRüsten,
SUM(p.bmk_11) AS ISTProduktion,
MONTH(prot_dat) AS Month
FROM
[hydra1].[hydadm].[v_auftrag_status] p
JOIN
[hydra1].[hydadm].[v_auftrags_bestand] b ON b.auftrag_nr = p.auftrag_nr
WHERE
p.masch_nr = 'GEORG'
AND a_status = 'E'
AND YEAR(prot_dat) = YEAR(CURRENT_TIMESTAMP)
GROUP BY
p.masch_nr, MONTH(prot_dat)) T1 ON T.Month = T1.Month
ORDER BY
T.Month
This is how it should look then:
Month | Prozess_Verfügbarkeit | Verfügbarkeit
------+-----------------------+--------------
1 | 344 | 4556
2 | 0 | 0
3 | 0 | 0
4 | 0 | 0
5 | 0 | 0
6 | 0 | 0
7 | 0 | 0
8 | 0 | 0
9 | 0 | 0
10 | 0 | 0
11 | 0 | 0
12 | 0 | 0
Thanks a lot.
Something like this ought to do it:
WITH months AS (
SELECT 1 AS month_number
UNION ALL SELECT 2
UNION ALL SELECT 3
...
UNION ALL SELECT 12
)
SELECT months.month_number
, your_query.thing_a
, your_query.thing_b
FROM months
LEFT
JOIN your_query
ON your_query.Month = months.month_number
;

Sum rows with same Id based on type and exlude where SUM = 0

I have this table MOVEMENTS:
Id | FatherId | MovementType | Quantity |
=================================================
1 | A | IN | 10 |
2 | A | IN | 5 |
3 | A | OUT | 5 |
4 | B | IN | 10 |
5 | B | OUT | 10 |
6 | C | IN | 5 |
I'm trying to get all the FatherId with the SUM of IN - OUT Movments > 0.
So the result would be:
FatherId | Total |
=========================
A | 10 |
C | 5 |
FatherId = B not showing because
SUM(MovementType = IN) - SUM (MovementType = OUT) = 0
I tried with
SELECT FatherId,
(SELECT (
SUM(CASE WHEN MovementType = 'IN' THEN Quantity ELSE 0 END) -
SUM(CASE WHEN MovementType = 'OUT' THEN Quantity ELSE 0 END)
)) AS Total
FROM MOVEMENTS
GROUP BY FatherId
ORDER BY FatherId
That gives me the result grouped by FatherId, but I'm not able to filter with Total > 0, and also, I'm unable to put this query in a Subquery like:
SELECT * FROM MOVEMENTS WHERE FatherId IN (SELECT ....) OFFSET ... FETCH NEXT ... ROWS ONLY
Is this doable without a stored procedure?
Thank you for any help
Why are you using a subquery? This should do what you want:
SELECT FatherId,
(SUM(CASE WHEN MovementType = 'IN' THEN Quantity ELSE 0 END) -
SUM(CASE WHEN MovementType = 'OUT' THEN Quantity ELSE 0 END)
) AS Total
FROM MOVEMENTS
GROUP BY FatherId
HAVING (SUM(CASE WHEN MovementType = 'IN' THEN Quantity ELSE 0 END) -
SUM(CASE WHEN MovementType = 'OUT' THEN Quantity ELSE 0 END)
) > 0;
You can also simplify the logic to use a single SUM():
SELECT FatherId,
SUM(CASE WHEN MovementType = 'IN' THEN Quantity
WHEN MovementType = 'OUT' THEN - Quantity
ELSE 0
END) AS Total
FROM MOVEMENTS
GROUP BY FatherId
HAVING SUM(CASE WHEN MovementType = 'IN' THEN Quantity
WHEN MovementType = 'OUT' THEN - Quantity
ELSE 0
END) > 0
ORDER BY FatherId;

SQL Update with Min and Sum and Group By

I'm looking to write an update that will correct a flag that's gone astray.
I'd like to set the PRIMARY_FLAG = 1 for the min(TABLE_ID) grouped by ACCOUNT_NUMBER where the sum(PRIMARY_FLAG) <> 1 grouped by ACCOUNT_NUMBER.
Here is what the table looks like now:
TABLE_ID ACCOUNT_NUMBER PRIMARY_FLAG
-------- -------------- ------------
1 ABC123 0
2 ABC123 1
3 ABC123 0
4 987XYZ 0
5 987XYZ 0
6 987XYZ 0
7 5A5B5C 1
8 5A5B5C 1
9 5A5B5C 0
10 5A5B5C 0
Here's what I want it to look like after the update:
TABLE_ID ACCOUNT_NUMBER PRIMARY_FLAG
-------- -------------- ------------
1 ABC123 0
2 ABC123 1
3 ABC123 0
4 987XYZ 1
5 987XYZ 0
6 987XYZ 0
7 5A5B5C 1
8 5A5B5C 0
9 5A5B5C 0
10 5A5B5C 0
Scenario 1 - TABLE_ID 1, 2, 3 with ACCOUNT_NUMBER = ABC123 is already correct and I do not want the update to touch it.
Scenario 2 - ACCOUNT_NUMBER = 987XYZ, none of the TABLE_ID have a PRIMARY_FLAG = 1, so update will set PRIMARY_FLAG = 1 where TABLE_ID = 4
Scenario 3 - ACCOUNT_NUMBER = 5A5B5C has multiple TABLE_ID = 1, so update would leave TABLE_ID 7 but SET PRIMARY_FLAG = 0 where TABLE_ID = 8
Help is appreciate!
Here is a generic method:
update t
set primary_flag = (case when t.id = (select min(t2.id)
from t t2
where t2.account_number = t.account_number
)
then 1 else 0
end)
where t.account_number in (select t2.acount_number
from t t2
group by t2.acount_number
having sum(t2.primary_flag) <> 1
);
This is standard SQL and will work in most databases. However, specific databases might have more concise or efficient methods of doing the same thing.
Join the table to the query that returns the minimum table_id for each account_number that needs to be updated:
update t
set t.primary_flag = case when t.table_id = g.minid then 1 else 0 end
from tablename t inner join (
select account_number, min(table_id) minid
from tablename
group by account_number
having sum(primary_flag) <> 1
) g on g.account_number = t.account_number;
See the demo.
Or with a CTE:
with cte as (
select *,
row_number() over (partition by account_number order by table_id) rn,
sum(primary_flag) over (partition by account_number) total
from tablename
)
update cte
set primary_flag = case when rn = 1 then 1 else 0 end
where total <> 1
See the demo.
To avoid unnecessary updates for the rows that do not need to be updated:
with cte as (
select *,
row_number() over (partition by account_number order by table_id) rn,
sum(primary_flag) over (partition by account_number) total
from tablename
)
update cte
set primary_flag = abs(primary_flag - 1)
where (total <> 1) and ((rn = 1 and primary_flag = 0) or (rn > 1 and primary_flag = 1))
See the demo.
Results:
> TABLE_ID | ACCOUNT_NUMBER | PRIMARY_FLAG
> -------: | :------------- | -----------:
> 1 | ABC123 | 0
> 2 | ABC123 | 1
> 3 | ABC123 | 0
> 4 | 987XYZ | 1
> 5 | 987XYZ | 0
> 6 | 987XYZ | 0
> 7 | 5A5B5C | 1
> 8 | 5A5B5C | 0
> 9 | 5A5B5C | 0
> 10 | 5A5B5C | 0

SQL separate the count of one column

I have a SQL table that contains three columns:
userId
userName
item
and I created this SQL query which will count all the items types of one user:
select
count(ItemID) as 'count of all items types',
userId,
userName
from
userTable
where
ItemID in (2, 3, 4)
and userId = 1
group by
userId, userName
The result will be like this:
+--------+----------+--------------------------+
| userId | userName | count of all items types |
+--------+----------+--------------------------+
| 1 | kim | 25 |
and I am looking for a way to separate the counting of itemes types, so the result should be like this:
+--------+----------+----------------+----------------+-----------------+
| userId | userName | count of item1 | count of item2 | count of item3 |
+--------+----------+----------------+----------------+-----------------+
| 1 | kim | 10 | 10 | 5 |
SELECT
userID,
userName,
SUM(CASE WHEN ItemID = 2 THEN 1 ELSE 0 END) AS count_of_item1,
SUM(CASE WHEN ItemID = 3 THEN 1 ELSE 0 END) AS count_of_item2,
SUM(CASE WHEN ItemID = 4 THEN 1 ELSE 0 END) AS count_of_item3
FROM
My_Table
GROUP BY
userID,
userName
This is called conditional aggregation. Use CASE for this.
With COUNT:
select
count(case when ItemID = 1 then 1 end) as count_item1,
count(case when ItemID = 2 then 1 end) as count_item2,
count(case when ItemID = 3 then 1 end) as count_item3
...
(then 1 could also be anything else except null, e.g. then 'count me'. This works because COUNT counts non-null values and when omitting the ELSE in CASE WHEN you get null. You could also explicitly add else null.)
Or with SUM:
select
sum(case when ItemID = 1 then 1 else 0 end) as count_item1,
sum(case when ItemID = 2 then 1 else 0 end) as count_item2,
sum(case when ItemID = 3 then 1 else 0 end) as count_item3
...
This is how you would do it :
select userId,
username,
SUM(CASE WHEN ItemID = '2' THEN 1 ELSE 0 END) AS Item2-Cnt,
SUM(CASE WHEN ItemID = '3' THEN 1 ELSE 0 END) AS Item3-Cnt,
SUM(CASE WHEN ItemID = '4' THEN 1 ELSE 0 END) AS Item4-Cnt
FROM userTable
GROUP BY userID, userName

SQL check if column contains specific values

I have a table like this:
id | Values
------------------
1 | a
1 | b
1 | c
1 | d
1 | e
2 | a
2 | a
2 | c
2 | c
2 | e
3 | a
3 | c
3 | b
3 | d
Now I want to know which id contains at least one of a, one of b and one of c.
This is the result I want:
id
--------
1
3
One method is aggregation with having:
select id
from t
where values in ('a', 'b', 'c')
group by id
having count(distinct values) = 3;
If you wanted more flexibility with the counts of each value:
having sum(case when values = 'a' then 1 else 0 end) >= 1 and
sum(case when values = 'b' then 1 else 0 end) >= 1 and
sum(case when values = 'c' then 1 else 0 end) >= 1
You can use grouping:
SELECT id
FROM your_table
GROUP BY id
HAVING SUM(CASE WHEN value = 'a' THEN 1 ELSE 0 END) >= 1
AND SUM(CASE WHEN value = 'b' THEN 1 ELSE 0 END) = 1
AND SUM(CASE WHEN value = 'c' THEN 1 ELSE 0 END) = 1;
or using COUNT:
SELECT id
FROM your_table
GROUP BY id
HAVING COUNT(CASE WHEN value = 'a' THEN 1 END) >= 1
AND COUNT(CASE WHEN value = 'b' THEN 1 END) = 1
AND COUNT(CASE WHEN value = 'c' THEN 1 END) = 1;