I have a StockLines table in MsSql 2017. It has columns:
Product, QuantityInStock, ReservedQuantity.
CocaCola 50 20
Now I need to display that info in 2 rows:
Product Quantity Reserved
CocaCola 20 true
CocaCola 30 false
Is it possible to achive using Pivot()? Am I on a correct path?
P.S I'm using Entity Framework Core.
You can easily do this using apply:
select t.product, v.quantity, v.reserved
from t cross apply
(values (t.ReservedQuantity, 'true'),
(t.QuantityInStock - t.ReservedQuantity, 'false')
) v(quantity, reserved);
I think you could do this using unpivot, but that is superfluous syntax. apply implements lateral joins, which are very powerful. This is just one example of how they can be used.
A simple to understand approach would be UNION:
SELECT product, reservedquantity as quantity, 'true' as reserved FROM stocklines
UNION ALL
SELECT product, quantityinstock-reservedquantity, 'false' FROM stocklines
I am wondering though if you have other lines in the table, detailing other reservations of the same product, are they also supposed to come from the total? Is the stock quantity the same across all lines? Hopefully all individual reservations of products are grouped together so stock lines table contains one row per product (or things get messy)
You also mentioned you're using ef core- see https://learn.microsoft.com/en-us/ef/core/querying/raw-sql
This technique could also be modified into a LINQ statement, perhaps something like(untested):
context.StockLines.Select(s => new[] { new {
s.Product,
Quantity = s.QuantityInStock - s.ReservedQuantity,
Reserved = false
},
new {
s.Product,
Quantity = s.ReservedQuantity,
Reserved = true
}
}).SelectMany().ToList()
The idea being we take each record and split it into an anonymous type representing the reserved and a type representing the unreserved as an array output from the first select then use selectmany to flatten the enumerable-of-arrays-of-type to an enunerable-of-type
This latter solution might need some tweaks; totally untested
You can use UNPIVOT
SELECT Product,
Quantity,
CASE Reserved
WHEN 'QuantityInStock' THEN 'FALSE'
ELSE 'TRUE'
END AS Reserved
FROM pro
UNPIVOT(
Quantity FOR Reserved IN (QuantityInStock, ReservedQuantity)
) AS t
Another way is without CASE operator, but with subquery and aliases:
SELECT Product,
Quantity,
Reserved
FROM (
SELECT product,
QuantityInStock AS [false],
ReservedQuantity AS [true]
FROM pro
) AS tt
UNPIVOT(Quantity FOR Reserved IN ([false], [true])) upvt
Related
I have the following query:
SELECT DISTINCT
CAB.CODPARC,
PAR.RAZAOSOCIAL,
BAI.NOMEBAI,
SUM(VLRNOTA) AS AMOUNT
FROM TGFCAB CAB, TGFPAR PAR, TSIBAI BAI
WHERE CAB.CODPARC = PAR.CODPARC
AND PAR.CODBAI = BAI.CODBAI
AND CAB.TIPMOV = 'V'
AND STATUSNOTA = 'L'
AND PAR.CODCID = 5358
GROUP BY
CAB.CODPARC,
PAR.RAZAOSOCIAL,
BAI.NOMEBAI
Which the result is this. Company names and neighborhood hid for obvious reasons
The query at the moment, for those who don't understand Latin languages, is giving me clients, company name, company neighborhood, and the total value of movements.
in the WHERE clause it is only filtering sales movements of companies from an established city.
But if you notice in the Select statement, the column that is retuning the value that aggregates the total amount of value of sales is a SUM().
My goal is to return only the company that have the maximum value of this column, if its a tie, display both of em.
This is where i'm struggling, cause i can't seem to find a simple solution. I tried to use
WHERE AMOUNT = MAX(AMOUNT)
But as expected it didn't work
You tagged the question with the whole bunch of different databases; do you really use all of them?
Because, "PL/SQL" reads as "Oracle". If that's so, here's one option.
with temp as
-- this is your current query
(select columns,
sum(vrlnota) as amount
from ...
where ...
)
-- query that returns what you asked for
select *
from temp t
where t.amount = (select max(a.amount)
from temp a
);
You should be able to achieve the same without the need for a subquery using window over() function,
WITH T AS (
SELECT
CAB.CODPARC,
PAR.RAZAOSOCIAL,
BAI.NOMEBAI,
SUM(VLRNOTA) AS AMOUNT,
MAX(VLRNOTA) over() AS MAMOUNT
FROM TGFCAB CAB
JOIN TGFPAR PAR ON PAR.CODPARC = CAB.CODPARC
JOIN TSIBAI BAI ON BAI.CODBAI = PAR.CODBAI
WHERE CAB.TIPMOV = 'V'
AND STATUSNOTA = 'L'
AND PAR.CODCID = 5358
GROUP BY CAB.CODPARC, PAR.RAZAOSOCIAL, BAI.NOMEBAI
)
SELECT CODPARC, RAZAOSOCIAL, NOMEBAI, AMOUNT
FROM T
WHERE AMOUNT=MAMOUNT
Note it's usually (always) beneficial to join tables using clear explicit join syntax. This should be fine cross-platform between Oracle & SQL Server.
Suppose I have a table like this in SQL Server 2017, let's call it "maps_and_cups"
some_code
quantity
big_map
6
tiny_map
5
big_cup
10
tiny_cup
4
I would like to know the best way to group the maps and cups into one, in this way.
some_code
quantity
maps
11
cups
14
I know that it is using "if" and "case", adding and comparing if it is a tiny_map, a big_map, and so on, I have seen several examples but I cannot make it compile.
You can indeed use a case when expression. For instance:
with base as
(select case some_code when 'big_map' then 'maps'
when 'tiny_map' then 'maps'
when 'big_cup' then 'cups'
when 'tiny_cup' then 'cups'
else 'other'
end grp,
quantity
from maps_and_cups)
select grp, sum(quantity) quantity from base group by grp;
However, if you're going to list each and every code explicitly, you might as well create a reference table for it:
some_code
grp
big_map
maps
tiny_map
maps
big_cup
cups
tiny_cup
cups
...and then join that table into your query:
select grp, sum(quantity)
from maps_and_cups a left join ref_maps_cups b on a.some_code = b.some_code
group by grp;
You can solve this task using "case" and "charindex" functions, like this:
declare
#t table (some_code varchar (20), quantity int)
insert into #t
values
('big_map', 6),
('tiny_map', 5),
('big_cup',10),
('tiny_cup', 4)
select
case
when charindex ('map', some_code)>0 then 'map'
when charindex ('cup', some_code)>0 then 'cup'
end some_code
,sum(quantity) quantity
from #t
group by
case
when charindex ('map', some_code)>0 then 'map'
when charindex ('cup', some_code)>0 then 'cup'
end
OUTPUT:
If you just want the right three characters for aggregating, you can use right():
select right(some_code, 3) + 's', sum(quantity)
from maps_and_cups
group by right(some_code, 3) + 's';
You are creating a problem for yourself as you're (probably) breaking the first normal form by storing non atomic values in the field "some_code". (Some field name i'd say. ;)
Why not separating the value into [ type ] and [ size ] ?
I have the following tables below and their schema:
INV
id, product code, name, ucost, tcost, desc, type, qoh
1,123,CPASS 700,1.00,5.00,CPASS 700 Lorem, COM,5
2,456,Shelf 5,2.00,6.00,Shelf 5 KJ, BR,3
GRP
id,type,desc
1,COM,COMPASS
2,BR,SHELF
Currently I have a query like this:
SELECT INV.*,GRP.DESCR AS CATEGORY
FROM INV LEFT JOIN GRP ON INV.TYPE = GRP.TYPE
WHERE INV.QOH = 0
There is no problems with that query.
Right now,I want to know the SUM of the TCOST of every INV record where their QOH is 0.
In this situation, does that I mean all I have to do is to write a separate query like the one below:
SELECT SUM(TCOST)
FROM INV
WHERE QOH = 0
Does it make any sense for me to try and combine those two queries as one ?
First understand that SUM is the aggregate function hence either you can run the Query like
(SELECT SUM(TCOST) FROM INV WHERE QOH=0) as total
This will return Sum of TCOST in INV Table for mentioned condition.
Another approach is finding the Sum based on the some column (e.g. Type)
you could write query like
SELECT Type , SUM(TCOST) FROM INV WHERE QOH=0 GROUP BY type ;
Its not clear on what criteria you want to sum . But I think above two approaches would provide you fare idea .
Mmm, you could maybe use a correlated query, though i'm not sure it's the best approach since I'm not sure I understand what your attempting to do:
SELECT INV.*,
GRP.DESCR AS CATEGORY ,
(SELECT SUM(TCOST) FROM INV WHERE QOH=0) as your_sum
FROM INV LEFT JOIN GRP ON INV.TYPE = GRP.TYPE
WHERE INV.QOH = 0
If you want only one value for the sum(), then your query is fine. If you want a new column with the sum, then use window functions:
SELECT INV.*, GRP.DESCR AS CATEGORY,
SUM(INV.TCOST) OVER () as sum_at_zero
FROM INV LEFT JOIN
GRP
ON INV.TYPE = GRP.TYPE
WHERE INV.QOH = 0;
It does not make sense to combine the queries by adding a row to the first one, because the columns are very different. A SQL result set requires that all rows have the same columns.
SELECT
CD.CountryID, CD.GrossLimit, CD.UnsecuredLimit,
SUM(LT1.Amount), SUM(LT1.Unsecured),
(100*SUM(LT1.Unsecured) / CD.UnsecuredLimit) as PercOverCountryLimit
FROM CountryDetail CD
INNER JOIN
(
SELECT CompanyName AS Company, CollateralSName as Collateral, SUM(Amount) AS Amount,
SUM(Usecured) AS Unsecured, LT.Date as Date, Max(CountryID) as CountryID
FROM Loanstotal LT
WHERE YearMonth = #YearMonth
GROUP BY CompanyName, CollateralSName, LT.Date
) LT1
GROUP BY CountryID, GrossLimit, UnsecuredLimit
ON CD.CountryID = LT1.CountryID
Well I see some possible problems right off
First your group by is in the incorrect place, it needs to be after the ON clause in the join not before it.
Possible issue with a typo: SUM(Usecured) should that be SUM(Unsecured)?
Next depending on what type of data is in the fields you are summing up, you may have a problem with integer math. An integer divided by an integer will give an integer result (3/2 = 1 for example) so you must convert one value to a numeric.
Other issues may be there depending on which version of SQL you are using, You need to specify which database backed to get the best answers. Mine are based on what SQL server would want.
I have a data set that lists the date and quantity of future stock of products. Occasionally our demand outstrips our future supply and we wind up with a negative future quantity. I need to factor that future negative quantity into previous supply so we don't compound the problem by overselling our supply.
In the following data set, I need to prepare for demand on 10-19 by applying the negative quantity up the chain until i'm left with a positive quantity:
"ID","SKU","DATE","SEASON","QUANTITY"
"1","001","2012-06-22","S12","1656"
"2","001","2012-07-13","F12","1986"
"3","001","2012-07-27","F12","-283"
"4","001","2012-08-17","F12","2718"
"5","001","2012-08-31","F12","-4019"
"6","001","2012-09-14","F12","7212"
"7","001","2012-09-21","F12","782"
"8","001","2012-09-28","F12","2073"
"9","001","2012-10-12","F12","1842"
"10","001","2012-10-19","F12","-12159"
I need to get it to this:
"ID","SKU","DATE","SEASON","QUANTITY"
"1","001","2012-06-22","S12","1656"
"2","001","2012-07-13","F12","152"
I have looked at using a while loop as well as an outer apply but cannot seem to find a way to do this yet. Any help would be much appreciated. This would need to work for sql server 2008 R2.
Here's another example:
"1","002","2012-07-13","S12","1980"
"2","002","2012-08-10","F12","-306"
"3","002","2012-09-07","F12","826"
Would become:
"1","002","2012-07-13","S12","1674"
"3","002","2012-09-07","F12","826"
You don't seem to get a lot of answers - so here's something if you won't get the right 'how-to do it in pure SQL'. Ignore this solution if there's anything SQLish - it's just a defensive coding, not elegant.
If you want to get a sum of all data with same season why deleting duplicate records - just get it outside, run a foreach loop, sum all data with same season value, update table with the right values and delete unnecessary entries. Here's one of the ways to do it (pseudocode):
productsArray = SELECT * FROM products
processed = array (associative)
foreach product in productsArray:
if product[season] not in processed:
processed[season] = product[quantity]
UPDATE products SET quantity = processed[season] WHERE id = product[id]
else:
processed[season] = processed[season] + product[quantity]
DELETE FROM products WHERE id = product[id]
Here is a CROSS APPLY - tested
SELECT b.ID,SKU,b.DATE,SEASON,QUANTITY
FROM (
SELECT SKU,SEASON, SUM(QUANTITY) AS QUANTITY
FROM T1
GROUP BY SKU,SEASON
) a
CROSS APPLY (
SELECT TOP 1 b.ID,b.Date FROM T1 b
WHERE a.SKU = b.SKU AND a.SEASON = b.SEASON
ORDER BY b.ID ASC
) b
ORDER BY ID ASC