Amazon offers their marketplace customers a CSV report which contains information about every article you sold. There are four rows per article, looking like this:
+----------------------+------------+-------------------+--------+
| orderid | amounttype | amountdescription | amount |
+----------------------+------------+-------------------+--------+
| 305-2406165-0572365 | ItemPrice | Principal | 2.98 |
| 305-2406165-0572365 | ItemPrice | Shipping | 3.89 |
| 305-2406165-0572365 | ItemFees | Commission | -0.45 |
| 305-2406165-0572365 | ItemFees | ShippingHB | -0.59 |
+----------------------+------------+-------------------+--------+
As you can see, every article has four rows, two for the actual selling price and two for the fees I have to pay to Amazon.
I import this CSV file into a SQL-table using MySQL. Selecting some data including the price looks like this:
SELECT DISTINCT
report.posteddate AS Date,
orders.OrderID,
orders.ExternalOrderID AS AZNr,
report.amount AS ArtPrice
FROM
report,
orders
WHERE
orders.ExternalOrderID = report.orderid
AND report.amountdescription = 'Principal'
AND report.transactiontype = 'Order'
ORDER by Date DESC
To get just the item price without the shipping I do a selection to get only the rows where amountdescription is "Principal". The transactiontype can be ignored in order to solve my problem.
What I want to do:
I want to extract both fields of amount where amounttype is "ItemFees", add them together and display the result as a single field. After this selection, a row should look like this:
+------------+---------+---------------------+----------+-------+
| Date | OrderID | AZNr | ArtPrice | Fees |
+------------+---------+---------------------+----------+-------+
| 24.07.2014 | 267720 | 305-2406165-0572365 | 2.98 | -1.04 |
+------------+---------+---------------------+----------+-------+
I tried to run a subquery for both rows with a selection to amounttype = "ItemFees" and combine the results, but I ended up in an error saying that my subquery returns more than one row. This is the query:
SELECT DISTINCT
report.posteddate AS Date,
orders.OrderID,
orders.ExternalOrderID AS AZNr,
report.amount AS ArtPrice,
(SELECT
SUM(report.amount)
FROM
report,
orders
WHERE
orders.ExternalOrderID = report.orderid
AND report.amountdescription = 'Commission') +
(SELECT
SUM(report.amount)
FROM
report,
orders
WHERE
orders.ExternalOrderID = report.orderid
AND report.amountdescription = 'ShippingHB') AS Fees
FROM
report,
orders
WHERE
orders.ExternalOrderID = report.orderid
AND report.amountdescription = 'Principal'
AND report.transactiontype = 'Order'
ORDER by Date DESC
Does anybody have an idea how to sum up two values from two different rows with the given condition (see WHERE-clause)? Also, I need to extract the shipping value, but I think this is the same question.
Thank you in advance.
you can calculate itemprice and itemfees with two queries and join them
select a.orderid, a.price, b.fees
from (select orderid, sum(amount) price from report where amounttype='ItemPrice' group by orderid) a
join (select orderid, sum(amount) fees from report where amounttype='ItemFees' group by orderid) b
on a.orderid = b.orderid
this asumes there is at least one row with itemprice and one row with itemfees. otherwise you should use an outer join.
Related
I have a table where it stores the types of discounts that a user can have.
Some users will get the standard discount, but some will get a bigger and better discount. For users who have the biggest and best discount, there will be two records in the database, one for the default discount and the other for the biggest and best discount. The biggest and best discount will be preferred in the search.
I would like to do a SELECT that would return the record with the highest discount and if you don't find it, return it with the standard discount for me to avoid making two queries in the database or having to filter in the source code.
Ex:
| id | user_id | country | discount | cashback | free_trial |
|-----------------------------------------------------------------------|
| 1 | 1 | EUA | DEFAULT | 10 | false |
| 2 | 1 | EUA | CHRISTMAS | 20 | true |
| 3 | 3 | EUA | DEFAULT | 10 | false |
SELECT *
FROM users
WHERE country = 'EUA'
AND (discount = 'CHRISTMAS' OR discount = 'DEFAULT');
In this example above for user 1 it would return the record with the discount equal to "CHRISTMAS" and for user 3 it would return "DEFAULT" because it is the only one that has. Can you help me please?
You can use the row_number() window function to do this. This function includes a PARTITION BY that lets you start the numbering over with each user, as well as it's own ORDER BY that lets you determine which rows will sort first within each user/partition.
Then you nest this inside another SELECT to limit to rows where the row_number() result is 1 (the discount that sorted best):
SELECT *
FROM (
SELECT *, row_number() OVER (PARTITION BY id, ORDER BY cashback desc) rn
FROM users
WHERE country = 'EUA'
) u
WHERE rn = 1
You could also use a LATERAL JOIN, which is usually better than the correlated join in the other answer, but not as good as the window function.
You can using GROUP BY to do it
SELECT u1.*
FROM users u1
JOIN
(
SELECT COUNT(id) AS cnt,user_id
FROM users WHERE country = 'EUA'
GROUP BY user_id
) u2 ON u1.user_id=u2.user_id
WHERE IF(u2.cnt=1,u1.discount='DEFAULT',u1.discount='CHRISTMAS')
DB Fiddle Demo
I'm looking for a way to select Category with lowest CustKey value as seen in below table 1. I want it to be displayed in a column called SignupCategory. I have also linked to my current SQL code which I cant make display the Category rather than the CustKey. I appreciate any suggestions as I am terribly stuck atm. Code is semi-dummy code. Note: Given that I have 10.000 CustomerIDs I would want all 10.000 customers SignupCategory.
Table 1:
| CustKey | CustomerID | Category |
|---------|------------|----------|
| 1 | Cust1 | Paying |
| 2 | Cust1 | Unpaying |
| 3 | Cust1 | Barred |
Result should show SignupCategory 'Paying'
SQL Code:
Select c.AgreementNumber, SignupCategory
FROM Customer c
Following is the WIP from another thread I found on stackoverflow:
INNER JOIN
(SELECT AgreementNumber, MIN(CustKey) As SignupCategory
FROM Customer
GROUP BY AgreementNumber, Category) X
ON c.AgreementNumber = X.AgreementNumber and c.Category = TRY_CONVERT(nvarchar,X.SignupCategory)
Following code works but displays CustKey (similar to what I found on stackoverflow):
INNER JOIN
(SELECT AgreementNumber, MIN(CustKey) As SignupCategory
FROM Customer
GROUP BY AgreementNumber) X
ON c.AgreementNumber = X.AgreementNumber AND c.CustKey = X.SignupCategory
For all customers respectively and if you have huge amount of data then use EXISTS instead of IN:-
SELECT category as SignupCategory FROM Customer WHERE CustKey IN (SELECT MIN(CustKey) FROM Customer group by CustomerID);
I would like to ask for help for an SQL request that give me values from two tables.
As an example I have one Table orders und one table processing.
I would like to make an report of the orders and the state of processing.
table orders
id | status | div
-------------------
1 | wating_r | div1
2 | closed | div2
3 | closed | div3
-
table processing:
id | order_id | type | date
----------------------------------------
1 | 2 | send_request | 15.01.15
2 | 2 | send_invoice | 30.01.15
3 | 1 | send_request | 01.02.15
4 | 3 | send_request2 | 10.02.15
5 | 3 | send_invoice | 15.02.15
what I would like to get:
order_id | status | date_request | date_request2 | date_invoice
--------------------------------------------------------------------------------
1 | waiting_r | 01.02.15 | NULL | NULL
2 | closed | 15.01.15 | NULL | 30.01.15
3 | closed | NULL | 10.02.15 | 15.02.15
my solution:
select orders.id as order_id, orders.status, IF(processing.type='send_invoice',date_format(processing.date, '%Y-%m-%d'), NULL) as date_invoice, IF(processing.type='send_request',date_format(processing.date, '%Y-%m-%d'), NULL) as date_request, IF(processing.type='send_request2',date_format(processing.date, '%Y-%m-%d'), NULL) as date_request2
from orders
inner join processing on orders.id = processing.order_id
where
case
when orders.status='closed' then processing.type='send_invoice'
when orders.status='waiting_r' then processing.type='send_request'
when orders.status='waiting_2'then processing.type='send_request2'
end
This works fine but with this IF statements I doesn't become the dates from the requests when an invoice was sent - I only get the date of the invoice.
Instead of the case request I tried the following but in this case I have more than one line for every order. When I tried to "group by" I have mixed data.
where
processing.type in ('send_invoice', 'send_request', 'completion_request_send')
You need to left-join the second table to the first three times, like so.
SELECT o.id AS order_id, o.status,
p1.date AS date_request,
p2.date AS date_request2,
p3.date AS date_invoice
FROM orders o
LEFT JOIN processing p1 ON o.id = p1.order_id AND p1.type='send_request'
LEFT JOIN processing p2 ON o.id = p2.order_id AND p2.type='send_request2'
LEFT JOIN processing p3 ON o.id = p3.order_id AND p3.type='send_invoice'
ORDER BY 1,2
This left-join with an id-matching criterion and the specific type choice pulls out the rows you need for each column. Left, as opposed to inner, join, allows the missing values to be shown as null.
Here it is, working. http://sqlfiddle.com/#!9/b8c74/5/0
This is a typical pattern for joining a key/value table where the (id/key) pairs are unique.
Edit Unfortunately it generates duplicate result set rows in situations where there's a duplicate key for a particular value. To deal with that, it's necessary to deduplicate the key/value table (processing) in this case.
This subquery will do that, taking the latest date value.
SELECT type, order_id, MAX(date) AS date
FROM processing
GROUP BY type, order_id
Then you have to use that subquery in the main query. This is where it would be good if MySQL had common table expressions. But it doesn't so things get kind of verbose.
SELECT o.id AS order_id, o.status,
p1.date AS date_request,
p2.date AS date_request2,
p3.date AS date_invoice
FROM orders o
LEFT JOIN (
SELECT type, order_id, MAX(date) AS date
FROM processing
GROUP BY type, order_id
) p1 ON o.id = p1.order_id AND p1.type='send_request'
LEFT JOIN (
SELECT type, order_id, MAX(date) AS date
FROM processing
GROUP BY type, order_id
) p2 ON o.id = p2.order_id AND p2.type='send_request2'
LEFT JOIN (
SELECT type, order_id, MAX(date) AS date
FROM processing
GROUP BY type, order_id
) p3 ON o.id = p3.order_id AND p3.type='send_invoice'
ORDER BY 1,2
I've written a query that builds a small table of information from a couple of data sources, it uses a self made table to reference the vehicle model for the final group by which is how the data needs to be viewed, however when I group by vehicle it misses out figures in the subquery column from the group by, i.e. if I group by Prefix it shows the correct numbers, grouped by Vehicle hides off some of the data.
The Prefix can relate to a couple of like vehicle models and hence the need to group by vehicle. Can anyone see what I've done wrong easily from the SQL query below please.
SELECT Vehicle, COUNT(`Chassis-No`) AS Stock,
ROUND((100/COUNT(`Chassis-No`)) * SUM(CASE WHEN `Vehicle Age` > '182' THEN 1 ELSE 0 END),1) AS Perc6Months,
ROUND((100/COUNT(`Chassis-No`)) * SUM(CASE WHEN `Vehicle Age` > '365' THEN 1 ELSE 0 END),1) AS Perc12Months,
(SELECT COUNT(VIN_Prefix) FROM Orderdownload
INNER JOIN VehicleMatrix ON (`VIN_Prefix` LIKE 'S%' AND Prefix = LEFT(`VIN_Prefix`,2)) OR (`VIN_Prefix` NOT LIKE 'S%' AND Prefix = LEFT(`VIN_Prefix`,1)) WHERE DealerCode = 'AA12345' AND `VIN_Prefix` = IF(LEFT(`Chassis-No`,1)='S',LEFT(`Chassis-No`,2),LEFT(`Chassis-No`,1))) As Qty
FROM DealerAgedStock
INNER JOIN VehicleMatrix AS VM
ON (`Chassis-No` LIKE 'S%' AND Prefix = LEFT(`Chassis-No`,2)) OR (`Chassis-No` NOT LIKE 'S%' AND Prefix = LEFT(`Chassis-No`,1))
WHERE `DL Dealer Code` = 'AA12345'
GROUP BY Vehicle
Grouped on Vehicle I get the following:
Vehicle | Perc6Months | Perc12Months | Qty
Mondeo | 37.5 | 0 | 2
Grouped on Prefix I get the following:
VIN_Prefix | Perc6Months | Perc12Months | Qty
S1 | 25 | 0 | 2
S2 | 50 | 0 | 2
Ideally it should look this this:
Vehicle | Perc6Months | Perc12Months | Qty
Mondeo | 37.5 | 0 | 4
Where S1 and S2 are relative to the Vehicle Mondeo, thus it gives me the first instance of subquery rather than adding them together.
My question is: why does the Group By not add the figures together properly from the subquery? I need it to add them to have the correct figures...
CREATE VIEW products_view
AS
Hi guys ! I've tree tables:
Products
Categories
Prices
A product belongs to one category and may has more prices.
consider this set of data:
Product :
id title featured category_id
1 | bread | yes | 99
2 | milk | yes | 99
3 | honey | yes | 99
Price :
id product_id price quantity
1 | 1 | 99.99 | 10
2 | 1 | 150.00 | 50
3 | 2 | 33.10 | 20
4 | 2 | 10.00 | 11
I need to create a view, a full list of products that for each product select the min price and its own category.
eg.
id title featured cat.name price quantity
1 | bread | yes | food | 99.99 | 10
I tried the following query but in this way I select only the min Price.price value but Price.quantity, for example, came from another row. I should find the min Price.price value and so use the Price.quantity of this row as correct data.
CREATE VIEW products_view
AS
SELECT `Prod`.`id`, `Prod`.`title`, `Prod`.`featured`, `Cat`.`name`, MIN(`Price`.`price`) as price,`Price`.`quantity`
FROM `products` AS `Prod`
LEFT JOIN `prices` AS `Price` ON (`Price`.`product_id` = `Prod`.`id`)
LEFT JOIN `categories` AS `Cat` ON (`Prod`.`category_id` = `Cat`.`id`)
GROUP BY `Prod`.`id`
ORDER BY `Prod`.`id` ASC
My result is:
id title featured cat.name price quantity
1 | bread | yes | food | 99.99 | **50** <-- wrong
Can you help me ? Thx in advance !
As documented under MySQL Extensions to GROUP BY (emphasis added):
In standard SQL, a query that includes a GROUP BY clause cannot refer to nonaggregated columns in the select list that are not named in the GROUP BY clause. For example, this query is illegal in standard SQL because the name column in the select list does not appear in the GROUP BY:
SELECT o.custid, c.name, MAX(o.payment)
FROM orders AS o, customers AS c
WHERE o.custid = c.custid
GROUP BY o.custid;
For the query to be legal, the name column must be omitted from the select list or named in the GROUP BY clause.
MySQL extends the use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause. This means that the preceding query is legal in MySQL. You can use this feature to get better performance by avoiding unnecessary column sorting and grouping. However, this is useful primarily when all values in each nonaggregated column not named in the GROUP BY are the same for each group. The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause. Sorting of the result set occurs after values have been chosen, and ORDER BY does not affect which values within each group the server chooses.
What you are looking for is the group-wise minimum, which can be obtained by joining the grouped results back to the table:
SELECT Prod.id, Prod.title, Prod.featured, Cat.name, Price.price, Price.quantity
FROM products AS Prod
LEFT JOIN categories AS Cat ON Prod.category_id = Cat.id
LEFT JOIN (
prices AS Price NATURAL JOIN (
SELECT product_id, MIN(price) AS price
FROM prices
GROUP BY product_id
) t
) ON Price.product_id = Prod.id
ORDER BY Prod.id