This is such a silly question but I've been battling with it all day.
I have two tables.
products
+-----+---------+
| SKU | Quantity|
+-----+---------+
| ABC | 90 |
| DEF | 40 |
| XYZ | 33 |
+-----+---------+
orders
+-----+----------+
| SKU | OrderID |
+-----+----------+
| ABC | aaa |
| ABC | yyy |
| DEF | xxx |
| DEF | bbb |
| DEF | ccc |
+-----+----------+
I want the output to show all of the fields from table 1 and match the sum of the count of SKU from Table 2. If there's no match from table 2, it should return null or 0. So the output should be:
+-----+---------+-------+
| SKU | Quantity| Count +
+-----+---------+-------+
| ABC | 90 | 2 |
| DEF | 40 | 3 |
| XYZ | 33 | |
+-----+---------+-------+
I wrote the following query (I'm using Laravel, so excuse the Laravel syntax)
$orders=Orders::groupBy('orders.SKU')
->selectRaw('sum(orders.quantity) as quantity_sum, orders.SKU');
$ordersAllProducts = DB::table('products')
->leftJoinSub($orders, 'orders', function ($join) {
$join->on('products.SKU','=','orders.SKU');
})->get();
It almost works, but I'm getting null for the SKU as shown below.
+-----+---------+-------+
| SKU | Quantity| Count +
+-----+---------+-------+
| ABC | 90 | 2 |
| DEF | 40 | 3 |
| | 33 | |
+-----+---------+-------+
Not completely familiar with this ORM but I believe you should say products.SKU here, not orders:
->selectRaw('sum(orders.quantity) as quantity_sum, orders.SKU');
^^^^^^^^^^
Every join has two sides; a left and a right. When you LEFT JOIN you effectively say "give every row on the left, and maybe a matching row on the right, or null if there isn't a match". Orders is from the right side of the join, i.e. the side that is occasionally null
I can provide you native query to get that 3 column result (mysql query)
SKU
Quantity
count ( "-" if count = 0 )
$sql = "select SKU, Quantity,
(select IF(count(*) > 0 , count(*), "-") from orders o where o.SKU =
p.SKU) as count
from products p" ;
$result = DB::select($sql);
dd($result);
If you want to return 0 for zero counts instead of "-" just remove (if condition) from query
Related
I have three tables that I'm trying to join.
I join the first two:
Purchase Orders
+-----+----------+
| ID | location |
+-----+----------+
| 1 | Canada |
| 2 | USA |
+-----+----------+
Stock
+-----+----------+----------------+
| SKU | OnOrder |PurchaseOrderID |
+-----+----------+----------------+
| ABC | 30 |2 |
| DEF | 40 |1 |
+-----+----------+----------------+
And my result is:
+-----+----------+----------------+
| SKU | OnOrder |location |
+-----+----------+----------------+
| ABC | 30 |USA |
| DEF | 40 |Canada |
+-----+----------+----------------+
And now I want to join this result with another table as shown below
ItemOrders
+-----+----------+----------------+
| SKU | Quantity |Location |
+-----+----------+----------------+
| ABC | 88 |USA |
| DEF | 99 |Mexico |
+-----+----------+----------------+
Where:
location of Join1 equals location of Table 3, and
SKU of Join 1 equals Table 3 .
If the match doesn't exist, it should make a new row
So I want my output to be:
+-----+----------+--------------------------+
| SKU | OnOrder |Quantity |Location |
+-----+----------+---------+----------------+
| ABC | 30 |88 |USA |
| DEF | 40 | |Canada |
| DEF | |99 |Mexico |
+-----+----------+---------+----------------+
I really can't wrap my head around this for some reason. I've tried two joins, a merge, two joins and a union. I'm trying to do this in laravel FWIW.
Your help is much appreciated!
I think you would create 2 queries where both return the same columns and then UNION them together.
Wrap that entire thing in a subquery and GROUP BY SKU, Location and use an aggregate function as a way to aggregate the 2 rows into one.
Aggregate functions usually ignore Null values so simply a MAX() might work.
Like,
SELECT SKU, Location, max(OnOrder) as OnOrder, max(Quantity) as Quantity
FROM
(
SELECT SKU, OnOrder, NULL as Quantity, Location
FROM {stuff to build output 1}
UNION ALL
SELECT SKU, NULL as OnOrder, Quantity, Location
FROM {stuff to build output 2}
) rs
GROUP BY SKU, Location;
I have two tables. Like this.
select * from extrafieldvalues;
+----------------------------+
| id | value | type | idItem |
+----------------------------+
| 1 | 100 | 1 | 10 |
| 2 | 150 | 2 | 10 |
| 3 | 101 | 1 | 11 |
| 4 | 90 | 2 | 11 |
+----------------------------+
select * from items
+------------+
| id | name |
+------------+
| 10 | foo |
| 11 | bar |
+------------+
I need to make a query and get something like this:
+--------------------------------------+
| idItem | valtype1 | valtype2 | name |
+--------------------------------------+
| 10 | 100 | 150 | foo |
| 11 | 101 | 90 | bar |
+--------------------------------------+
The quantity of types of extra field values is variable, but every item ALWAYS uses every extra field.
If you have only two fields, then left join is an option for this:
select i.*, efv1.value as value_1, efv2.value as value_2
from items i left join
extrafieldvalues efv1
on efv1.iditem = i.id and
efv1.type = 1 left join
extrafieldvalues efv2
on efv1.iditem = i.id and
efv1.type = 2 ;
In terms of performance, two joins are probably faster than an aggregation -- and it makes it easier to bring in more columns from items. One the other hand, conditional aggregation generalizes more easily and the performance changes by little as more columns from extrafieldvalues are added to the select.
Use conditional aggregation
select iditem,
max(case when type=1 then value end) as valtype1,
max(case when type=2 then value end) as valtype2,name
from extrafieldvalues a inner join items b on a.iditem=b.id
group by iditem,name
Problem description
Let the tables C and V have those values
>> Table V <<
| UnID | BillID | ProductDesc | Value | ... |
| 1 | 1 | 'Orange Juice' | 3.05 | ... |
| 1 | 1 | 'Apple Juice' | 3.05 | ... |
| 1 | 2 | 'Pizza' | 12.05 | ... |
| 1 | 2 | 'Chocolates' | 9.98 | ... |
| 1 | 2 | 'Honey' | 15.98 | ... |
| 1 | 3 | 'Bread' | 3.98 | ... |
| 2 | 1 | 'Yogurt' | 8.55 | ... |
| 2 | 1 | 'Ice Cream' | 7.05 | ... |
| 2 | 1 | 'Beer' | 9.98 | ... |
| 2 | 2 | 'League of Legends RP' | 40.00 | ... |
>> Table C <<
| UnID | BillID | ClientName | ... |
| 1 | 1 | 'Alexander' | ... |
| 1 | 2 | 'Tom' | ... |
| 1 | 3 | 'Julia' | ... |
| 2 | 1 | 'Tom' | ... |
| 2 | 2 | 'Alexander' | ... |
Table C have the values of each product, which is associated with a bill number. Table V has the relationship between the client name and the bill number. However, the bill number has a counter that is dependent on the UnId, which is the store unity ID. That being said, each store has it`s own Bill number 1, number 2, etc. Also, the number of bills from each store are not equal.
Solution description
I'm trying to make select between the C left join V without sucess. Because each BillID is dependent on the UnID, I have to make the join considering the concatenation between those two columns.
I've used this script, but it gives me an error.
SELECT
SUM(C.Value),
V.ClientName
FROM
C
LEFT JOIN
V
ON
CONCAT(C.UnID, C.BillID) = CONCAT(V.UnID, V.BillID)
GROUP BY
V.ClientName
and SQL server returns me this 'CONCAT' is not a recognized built-in function name.
I'm using Microsoft SQL Server 2008 R2
Is the use of CONCAT wrong? Or is it the way I tried to SELECT? Could you give me a hand?
[OBS: The tables I've present you are just for the purpose of explaining my difficulties. That being said, if you find any errors in the explanation, please let me know to correct them.]
You should be joining on the equality of the UnID and BillID columns in the two tables:
SELECT
c.ClientName,
COALESCE(SUM(v.Value), 0) AS total
FROM C c
LEFT JOIN V v
ON c.UnID = v.UnID AND
c.BillID = v.BillID
GROUP BY
c.ClientName;
In theory you could try joining on CONCAT(UnID, BillID). However, you could run into problems. For example, UnID = 1 with BillID = 23 would, concatenated together, be the same as UnID = 12 and BillID = 3.
Note: We wrap the sum with COALESCE, because should a given client have no entries in the V table, the sum would return NULL, which we then replace with zero.
concat is only available in sql server 2012.
Here's one option.
SELECT
SUM(C.Value),
V.ClientName
FROM
C
LEFT JOIN
V
ON
cast(C.UnID as varchar(100)) + cast(C.BillID as varchar(100)) = cast(V.UnID as varchar(100)) + cast(V.BillID as varchar(100))
GROUP BY
V.ClientName
I have a source table that has a few different prices for each product (depending on the order quantity). Those prices are listed vertically, so each product could have more than one row to display its prices.
Example:
ID | Quantity | Price
--------------------------
001 | 5 | 100
001 | 15 | 90
001 | 50 | 80
002 | 10 | 20
002 | 20 | 15
002 | 30 | 10
002 | 40 | 5
The other table I have is the result table in which there is only one row for each product, but there are five columns that each could contain the quantity and price for each row of the source table.
Example:
ID | Quantity_1 | Price_1 | Quantity_2 | Price_2 | Quantity_3 | Price_3 | Quantity_4 | Price_4 | Quantity_5 | Price_5
---------------------------------------------------------------------------------------------------------------------------
001 | | | | | | | | | |
002 | | | | | | | | | |
Result:
ID | Quantity_1 | Price_1 | Quantity_2 | Price_2 | Quantity_3 | Price_3 | Quantity_4 | Price_4 | Quantity_5 | Price_5
---------------------------------------------------------------------------------------------------------------------------
001 | 5 | 100 | 15 | 90 | 50 | 80 | | | |
002 | 10 | 20 | 20 | 15 | 30 | 10 | 40 | 5 | |
Here is my Python/SQL solution for this (I'm fully aware that this could not work in any way, but this was the only way for me to show you my interpretation of a solution to this problem):
For Each result_ID In result_table.ID:
Subselect = (SELECT * FROM source_table WHERE source_table.ID = result_ID ORDER BY source_table.Quantity) # the Subselect should only contain rows where the IDs are the same
For n in Range(0, len(Subselect)): # n (index) should start from 0 to last row - 1
price_column_name = 'Price_' & (n + 1)
quantity_column_name = 'Quantity_' & (n + 1)
(UPDATE result_table
SET result_table.price_column_name = Subselect[n].Price, # this should be the price of the n-th row in Subselect
result_table.quantity_column_name = Subselect[n].Quantity # this should be the quantity of the n-th row in Subselect
WHERE result_table.ID = Subselect[n].ID)
I honestly have no idea how to do this with only SQL or VBA (those are the only languages I'd be able to use -> MS-Access).
This is a pain in MS Access. If you can enumerate the values, you can pivot them.
If we assume that price is unique (or quantity or both), then you can generate such a column:
select id,
max(iif(seqnum = 1, quantity, null)) as quantity_1,
max(iif(seqnum = 1, price, null)) as price_1,
. . .
from (select st.*,
(select count(*)
from source_table st2
where st2.id = st.id and st2.price >= st.price
) as seqnum
from source_table st
) st
group by id;
I should note that another solution would use data frames in Python. If you want to take that route, ask another question and tag it with the appropriate Python tags. This question is clearly a SQL question.
I require a SQL query to work out every product combination.
I have three product categories (game, accessory, upgrade) and products assigned to each of these three categories:
+----+------------+-----------+------------+
| id | category | product | prod_code |
+----+------------+-----------+------------+
| 1 | game | GTA | 100 |
| 2 | game | GTA1 | 200 |
| 3 | game | GTA2 | 300 |
| 4 | accessory | Play Pad | 400 |
| 5 | accessory | Xbox Pad | 500 |
| 6 | upgrade | Memory | 600 |
| 6 | upgrade | drive | 700 |
+----+------------+-----------+------------+
I want to take one product from each of the categories and work out every single combination:
+----+--------------+
| id | combinations |
+----+--------------+
| 1 | 100,400,600 |
| 2 | 100,500,600 |
| 3 | 100,400,700 |
| 4 | 100,500,700 |
| ? | etc |
+----+--------------+
How would I go about doing this?
Thanks in advance, Stuart
Use a CROSS JOIN:
SELECT CONCAT(t1.[prod_code], ',',
t2.[prod_code], ',',
t3.[prod_code])
FROM (
SELECT [prod_code]
FROM mytable
WHERE category = 'game') AS t1
CROSS JOIN (
SELECT [prod_code]
FROM mytable
WHERE category = 'accessory') AS t2
CROSS JOIN (
SELECT [prod_code]
FROM mytable
WHERE category = 'upgrade') AS t3
ORDER BY t1.[prod_code], t2.[prod_code], t3.[prod_code]
CROSS JOIN of derived tables, one for each category, produces the following cartesian product: 'game' products x 'accessory' products x 'upgrade' products
Demo here