Condition row total as a column in another table using MySQL - mysql

Firstly, I apologize for the terrible wording, but I'm not sure how to describe what I'm doing...
I have a table of computer types (id, type, name), called com_types
id | type | name
1 | 1 | Dell
2 | 4 | HP
In a second table, I have each individual computer, with a column 'type_id' to denote what type of computer it is, called com_assets
id | type_id | is_assigned
1 | 4 | 0
2 | 1 | 1
I'd like to create a view that shows each computer type, and how many we have on hand and in use, and a total, so the outcome would be
id | type | name | on_hand | in_use | total |
1 | 1 | Dell | 0 | 1 | 1 |
2 | 4 | HP | 1 | 0 | 1 |
As you can see, the on_hand, in_use, and total columns are dependent on the type_id and is_assigned column in the second table.
So far I have tried this...
CREATE VIEW test AS
SELECT id, type, name,
( SELECT COUNT(*) FROM com_assets WHERE type_id = id AND is_assigned = '0' ) as on_hand,
( SELECT COUNT(*) FROM com_assets WHERE type_id = id AND is_assigned = '1' ) as in_use,
SUM( on_hand + in_use ) AS total
FROM com_types
But all this returns is one column with all correct values, except the total equals ALL of the computers in the other table. Will I need a trigger to do this instead?

on_hand is the count of assigned = 0, and in_use is the count of assigned = 1. You can count them together, without the correlated subqueries, like this:
SELECT
com_types.id,
com_types.type,
com_types.name,
COUNT(CASE WHEN com_assets.is_assigned = 0 THEN 1 END) AS on_hand,
COUNT(CASE WHEN com_assets.is_assigned = 1 THEN 1 END) AS in_use,
COUNT(*) AS total
FROM com_types
JOIN com_assets ON com_types.id = com_assets.id
GROUP BY
com_types.id,
com_types.type,
com_types.name

Related

How do i add another COUNT statement with different condition to sql query

I have 2 tables.
1st table: duels
| duelId | user1Id | user2Id | gameId | winnerId |
2nd table: usergameprogress
| usergameprogressId | userId | gameId | gameStar |
Given an userId, I would like to get duel count, gameStar, win count for each gameId.
Example return:
| duelCount | duelWinCount | gameStar | gameId |
I have managed to get duelCount, gameStar and gameId given a userId but I couldn't add duelWinCount to my query result. How do I do that ?
My query:
SELECT
COUNT(d1.duelId) AS duelCount,
usergameprogress.gameId, usergameprogress.gameStar
FROM
duels d1
JOIN
usergameprogress ON (usergameprogress.gameId = d1.gameId)
WHERE
d1.user1Id = "gkfurcwsi033qzxg0u2bmj1ekebsklej"
OR d1.user2Id = "gkfurcwsi033qzxg0u2bmj1ekebsklej"
GROUP BY
usergameprogress.gameId
EDIT:
solved thanks to comment use sum instead of count
SELECT sum(case when d1.user1Id = 'gkfurcwsi033qzxg0u2bmj1ekebsklej' OR d1.user2Id="gkfurcwsi033qzxg0u2bmj1ekebsklej" then 1 else 0 end) AS totalDuelCount,sum(case when winnerId="gkfurcwsi033qzxg0u2bmj1ekebsklej" then 1 else 0 end) AS duelWinCount,usergameprogress.gameId,usergameprogress.gameStar FROM duels d1 JOIN usergameprogress ON (usergameprogress.gameId = d1.gameId) GROUP BY usergameprogress.gameId

MySQL keep first occurrence of value set duplicates to 0

I'm working with invoice data that has multiple line items for each invoice. The issue is that the tax field has the entire invoice tax value on each line item as shown below:
| Invoice | Item | Amount | Tax |
|---------|------|--------|-----|
| 1 | A | 10 | 6 |
| 1 | B | 20 | 6 |
| 1 | C | 30 | 6 |
What I would want
| Invoice | Item | Amount | Tax |
|---------|------|--------|-----|
| 1 | A | 10 | 6 |
| 1 | B | 20 | 0 |
| 1 | C | 30 | 0 |
So essentially I want to keep the tax value on the row with the first occurrence of the invoice number and then every occurrence of that invoice number after I want to set the tax value to 0. Is this even possible?
If you are running MySQL 8.0, you can use window function row_number() to identify the "first" item per invoice, and a case expression to replace tax with 0 on other rows of the group:
select
invoice,
item,
amount
case when row_number() over(partition by invoice order by item) = 1
then tax
else 0
end as amount
from mytable
If you wanted an update statement:
update mytable t
inner join (
select invoice, item,
row_number() over(partition by invoice order by item) rn
from mytable
) t1 on t1.invoice = t.invoice and t1.item = t.item
set t.tax = 0
where t1.rn > 1
Considerin the first occurence determined by the value of Item column, you can use an Update Statement with a self-join where only the least letter value won't be updated through use of inequality logic :
UPDATE tab t1
JOIN tab t2
ON t2.Invoice = t1.Invoice
AND t2.Item < t1.Item
SET t1.Tax = 0
P.S.: This method works also for pre 8 versions of database.
Demo

First Unique Sql row

I have a MySql table of users order and it has columns such as:
user_id | timestamp | is_order_Parent | Status |
1 | 10-02-2020 | N | C |
2 | 11-02-2010 | Y | D |
3 | 11-02-2020 | N | C |
1 | 12-02-2010 | N | C |
1 | 15-02-2020 | N | C |
2 | 15-02-2010 | N | C |
I want to count number of new custmer per day defined as: a customer who orders non-parent order and his order status is C AND WHEN COUNTING A USER ONCE IN A DAY WE DONT COUNT HIM FOR OTHER DAYS
An ideal resulted table will be:
Timestamp: Day | Distinct values of User ID
10-02-2020 | 1
11-02-2010 | 1
12-02-2010 | 0 <--- already counted user_id = 1 above, so no need to count it here
15-02-2010 | 1
table name is cscart_orders
If you are running MySQL 8.0, you can do this with window functions an aggregation:
select timestamp, sum(timestamp = timestamp0) new_users
from (
select
t.*,
min(case when is_order_parent = 'N' and status = 'C' then timestamp end) over(partition by user_id) timestamp0
from mytable t
) t
group by timestamp
The window min() computes the timestamp when each user became a "new user". Then, the outer query aggregates by date, and counts how many new users were found on that date.
A nice thing about this approach is that it does not require enumerating the dates separately.
You can use two levels of aggregation:
select first_timestamp, count(*)
from (select t.user_id, min(timestamp) as first_timestamp
from t
where is_order_parent = 'N' and status = 'C'
group by t.user_id
) t
group by first_timestamp;

Output row with null value only if there isn't the same row with a non-null value

Trying to formulate my question as good as I can...
I have a pricing table with historic data in it. So per item there is a price for certain dates. The difficulty is that the rows also have a type (1 = price for purchase order, 2 = sales order) and a VendorID.
That VendorID can be filled on a row: the price on that row is then for that specific vendor. If there is no row for a certain item in this table that has a VendorID, but it does have a row where VendorID is null, that row should be in the result.
So, if there are two rows in the result, one with a VendorID value and one with the VendorID being null, the row with the value should be in the result set and the row with the null value may not be in the result set.
Also, the result set should only contain the prices that are the newest, so i have to take in account the 'FromDate'.
The name of the column 'VendorID' is not well chosen because the rows with type = 2 are for sales orders, but let's forget about that for now ;-)
If I want all items for type = 1, would like to have the following result set:
ID | ItemID | FromDate | Type | VendorID | price
------------------------------------------------
1 | 1. | 2020-01-01 | 1 | 97 | 2.45
9 | 2 | 2020-02-15 | 1 | 97 | 3.88
7 | 3 | 2020-01-01 | 1 | 97 | 2.55
Suppose IDs 3,4 and 9 wheren't in the table (so, no pricing for item 2 for specific VendorID 97), the result should be:
ID | ItemID | FromDate | Type | VendorID | price
------------------------------------------------
1 | 1 | 2020-01-01 | 1 | 97 | 2.45
13 | 2 | 2020-01-01 | 1 | NULL | 999.45
7 | 3 | 2020-01-01 | 1 | 97 | 2.55
For ItemID 2 this would mean that there isn't a specific price set for VendorID 97 but there is a general price set (VendorID is null) and this price should now be placed in the result set.
I hope I explained it more clearly now....
I've written loads of queries now and also googled a lot but I cannot find how to make it do what I want. I tried distinct, sorting, but no luck. Must be something simple but I can't find it.
Up until now I have the following Mysql query but a) it outputs both the rows where VendorID is null and where it has value and b) I think its very overcomplicated but can't figure out how to make it simpler and faster.
SELECT I.ItemID, I.Name, b.vendorID, b.max_date, IP.Price, T.Percentage
FROM Items I
JOIN
(
SELECT ItemID, VendorID, MAX(FromDate) max_date, type
FROM ItemPrices
WHERE
Type = 1 AND
FromDate < '2020-02-30' AND
VendorID = (SELECT ID FROM Vendors WHERE VendorID = 'V001') OR VendorID IS NULL
GROUP BY ItemID, VendorID
) b ON I.ID = b.ItemID
JOIN ItemPrices IP ON IP.ItemID = b.ItemID AND IP.Type = b.type AND IP.FromDate = b.max_date AND (IP.VendorID = b.VendorID OR IP.VendorID IS NULL)
LEFT JOIN TaxCodes T ON T.ID =
(
SELECT TC.TaxCodeID FROM TaxCombinations TC
WHERE
TC.Direction = 1 AND
TC.TaxAreaID = (SELECT TaxArea FROM Vendors WHERE ID = (SELECT ID FROM Vendors WHERE VendorID = 'V001') )
AND TC.ItemTaxID = I.ItemTaxID
)
ORDER BY I.ItemID ASC
Also looked at the following urls but still don't know what to do:
Distinct rows with non-null values taking precedence over nulls
Select the non-null value if exists else null, but always select the row
Can someone please help me?
With NOT EXISTS:
select t.* from tablename t
where t.vendorid is not null
or not exists (
select 1 from tablename
where itemid = t.itemid and vendorid is not null
)
See the demo.

Select the same column multiple times with MySQL

Assuming I have something like this :
MySQL Table
Date | Name | Val
22/11 | a | 1
22/11 | b | 2
22/11 | a | 3
22/11 | a | 4
23/11 | b | 1
23/11 | a | 2
23/11 | a | 3
23/11 | a | 5
I need a query to have on one column the sum of the values for each day when Name = 'a' and an other column for the sum of all the values (for each day too).
With my example, the result would be something like this :
Date | a.Total | Total
22/11 | 8 | 10
23/11 | 10 | 11
I tried something like this :
SELECT date, SUM(Val) AS a.Total, SUM(Val) AS Total FROM tbl1 Where Name = 'a'
The point is that I need to specify a WHERE clause to get the "a.total" values (WHERE Name = 'a') but I don't want it to be apply to get the total.
I also tried queries with Left Join but it didn't work.
Any help is much appreciated.
You should use GROUP BY and CASE inside of the first SUM()
SELECT date,
SUM( CASE WHEN Name='a'
THEN Val
ELSE 0
END) AS a_Total,
SUM(Val) AS Total
FROM tbl1
GROUP BY `Date`
SQLFiddle demo
This is a type of problem called cross-tabbing (see https://www.simple-talk.com/sql/t-sql-programming/creating-cross-tab-queries-and-pivot-tables-in-sql/)
What you're after is the use of a CASE statement to allow you to sum values only when a condition is met.
SELECT date, SUM(CASE WHEN Name='a' then Val end) AS a.Total, SUM(Val) AS Total FROM tbl1 GROUP BY date