Count Calculation using PIVOT IN SQL - sql

I am trying to calculate the number of stock and no-stock using pivot. Is this possible with SQL Server 2008?
Table:
DECLARE #MYTABLE TABLE
(
ID INT,
PRODUCT VARCHAR (35),
SKU INT,
NEWPRICE DECIMAL(10,5),
OLDPRICEEX DECIMAL(10,5),
REMARKS VARCHAR (35)
)
INSERT #MYTABLE
SELECT 438, 'RESISTOR', 43822, 12.66, 11.13, 'STOCK' UNION ALL
SELECT 438, 'RESISTOR', 43870, 11.99, 12.30, 'OUTS-STOCK' UNION ALL
SELECT 719, 'INDUCTOR', 71911, 666.66, 764.16, 'OUTS-STOCK' UNION ALL
SELECT 101, 'CAPACITOR',10159, 22.66, 19.12, 'STOCK' UNION ALL
SELECT 101, 'CAPACITOR',10159, 19.32, 18.19, 'STOCK'
Expected output
ID | PRODUCT | STOCK | OUT-STOCK
----+-----------+-------+----------
438 | RESISTOR | 1 | 1
719 | INDUCTOR | 0 | 1
101 | CAPACITOR | 2 | 0
Thanks

No need of Pivot. USe below query.
SELECT ID,Product, SUM(case when REMARKS ='STOCK' then 1 else 0 end) as STOCK,
SUM(case when REMARKS ='OUTS-STOCK' then 1 else 0 end) as [OUT-STOCK]
FROM #MYTABLE
GROUP BY ID,Product

Using PIVOT:
Select * from
(Select ID As ForCnt, ID, PRODUCT, REMARKS from #MYTABLE) a
PIVOT
(
COUNT(ForCnt) FOR REMARKS IN ([STOCK], [OUTS-STOCK])
) x

Related

how to separate and sum 2 columns based on condition

I'm doing a select statement and I have a column I would like to separate into 2 columns based on their type, and then get the sum of the amounts grouped by an ID
I want all the gold and platinum types in one column, and all the silver and bronze in a 2nd column, then summed and grouped by the ID so it looks like this :
I tried doing a union like this:
SELECT
ID,
SUM(Amount) AS "Gold/Platinum",
0 AS "Bronze/Silver"
FROM
table
WHERE
Type IN ('gold', 'platinum')
GROUP BY
ID
UNION ALL
SELECT
ID,
SUM(Amount) AS "Bronze/Silver",
0 AS "Gold/Platinum"
FROM
table
WHERE
Type IN ('bronze', 'silver')
GROUP BY
ID
The gold/platinum column will be correct, but I get nothing in the bronze/silver column
Use conditional aggregation:
select id,
sum(case when Type in ('gold', 'platinum') then amount else 0 end) as gold_platinum,
sum(case when Type in ('bronze', 'silver') then amount else 0 end) as bronze_silver
from t
group by id
order by id;
You can run this in SSMS:
DECLARE #data TABLE( [ID] INT, [Type] VARCHAR(10), [Amount] INT );
INSERT INTO #data ( [ID], [Type], [Amount] ) VALUES
( 1, 'gold', 100 )
, ( 1, 'gold', 50 )
, ( 1, 'bronze', 75 )
, ( 2, 'silver', 10 )
, ( 2, 'bronze', 20 )
, ( 3, 'gold', 35 )
, ( 4, 'silver', 20 )
, ( 4, 'platinum', 30 );
SELECT
[ID]
, SUM( CASE WHEN [Type] IN ( 'gold', 'platinum' ) THEN Amount ELSE 0 END ) AS [Gold/Platinum]
, SUM( CASE WHEN [Type] IN ( 'bronze', 'silver' ) THEN Amount ELSE 0 END ) AS [Bronze/Silver]
FROM #data
GROUP BY [ID]
ORDER BY [ID];
Returns
+----+---------------+---------------+
| ID | Gold/Platinum | Bronze/Silver |
+----+---------------+---------------+
| 1 | 150 | 75 |
| 2 | 0 | 30 |
| 3 | 35 | 0 |
| 4 | 30 | 20 |
+----+---------------+---------------+

How to calculate average in SQL?

lets say I have the following table:
**FOOD** | **AMOUNT**
Bread | 2
Banana | 5
Pizza | 4
Apple | 57
Mandarin| 9
Orange | 8
Final result:
Bread | Percentage Of Total
Banana | percentage of total
etc
etc
I tried it in every single way, but couldn't find a solution. I hope someone can help me.
Using ANSI SQL (and SQL Server supports this syntax), you can do:
select food, sum(amount),
sum(amount) / sum(sum(amount)) over () as proportion_of_total
from t
group by food;
Note: Some databases do integer division, so you may need to convert to a floating point or fixed point type.
We can also try like below-
DECLARE #tbl AS TABLE
(
food VARCHAR(15)
,amount INT
)
INSERT INTO #tbl VALUES
('bread', 2)
,('banana', 5)
,('pizza', 4)
,('apple', 57)
,('mandarin', 9)
,('orange', 8)
SELECT
DISTINCT
food
,SUM(amount) OVER() TotalAmount
,SUM(amount) OVER (PARTITION BY food) PerFoodTotal
,CAST(SUM(amount) OVER (PARTITION BY food) * 100. / (SUM(amount) OVER()) AS DECIMAL(10,2)) [Percentage Of Total]
FROM #tbl
OUTPUT
food TotalAmount PerFoodTotal Percentage Of Total
--------------- ----------- ------------ ---------------------------------------
apple 85 57 67.06
banana 85 5 5.88
bread 85 2 2.35
mandarin 85 9 10.59
orange 85 8 9.41
pizza 85 4 4.71
(6 row(s) affected)
You can try something like this:
declare #tbl as table (
food varchar(15)
,amount int
)
insert into #tbl values
('bread', 2)
,('banana', 5)
,('pizza', 4)
,('apple', 57)
,('mandarin', 9)
,('orange', 8)
select SUM(amount) from #tbl
select
food
,SUM(amount) as [food amount]
,(SUM(cast(amount as numeric(18,2))) / (select sum(cast(amount as numeric(18,2))) from #tbl)) * 100 as [Percentage Of Total]
,(select sum(amount) from #tbl) as total
from #tbl
group by food
Here you got a way fo getting the PercentageOfTotal, asuming that the sum of all will not be 0
DECLARE #total INT = (SELECT SUM(AMOUNT) FROM Table1)
SELECT FOOD, CAST((CAST((100 * AMOUNT) AS DECIMAL (18,2)) / #total ) AS DECIMAL(18,2)) AS PercentageOfTotal from Table1
SQL Fiddle
MS SQL Server 2014 Schema Setup:
CREATE TABLE MusicGenres (name varchar(10)) ;
INSERT INTO MusicGenres (name)
VALUES ('Pop'),('Techno'),('Trance'),('trap'),('Hardcore'),('Electro') ;
CREATE TABLE Table2 (SongID int, MusicGenres varchar(10)) ;
INSERT INTO Table2 (SongID, MusicGenres)
VALUES (1,'Hardcore')
,(2,'Hardcore')
,(3,'Pop')
,(4,'Trap')
,(5,'Hardcore')
,(6,'Pop')
,(7,'Electro')
,(8,'Electro')
,(9,'Pop')
,(10,'Pop')
,(11,'Pop')
;
Query 1:
SELECT s1.name
, s1.recCount
, ( s1.recCount / CAST( ( SUM(recCount) OVER() ) AS decimal(5,2) ) )*100 AS pct
FROM (
SELECT m.name
, count(t.SongID) AS recCount
FROM MusicGenres m
LEFT OUTER JOIN Table2 t ON m.name = t.MusicGenres
GROUP BY m.name
) s1
Could be shortened to
SELECT m.name
, count(t.SongID) AS recCount
, ( count(t.SongID) / CAST( ( SUM(count(t.SongID)) OVER() ) AS decimal(5,2) )
)*100 AS pct
FROM MusicGenres m
LEFT OUTER JOIN Table2 t ON m.name = t.MusicGenres
GROUP BY m.name
Results:
| name | recCount | pct |
|----------|----------|---------|
| Electro | 2 | 18.1818 |
| Hardcore | 3 | 27.2727 |
| Pop | 5 | 45.4545 |
| Techno | 0 | 0 |
| Trance | 0 | 0 |
| trap | 1 | 9.0909 |

SQL query create cross column

I have this table
customer | product | quantity
-------------------------------
CLI01 | A | 10
CLI01 | B | 20
CLI02 | A | 31
CLI03 | A | 10
CLI03 | C | 12
and I want to create in SQL Server this output:
customer | crossProduct | quantity
-----------------------------------
CLI01 | A+B | 30
CLI02 | Only A | 31
CLI03 | B+C | 22
Thanks in advance
Niko
If you only care about two products, then this is simple aggregation:
select customer,
(case when count(distinct product) > 2 then 'Lots of Products'
when min(product) = max(product) then 'Only ' + min(product)
else min(product) + '+' + max(product)
end) as crossproduct,
sum(quantity)
from t
group by customer;
If you care about more than two products, then you'll need to do aggregation string concatenation. That is a bit painful in SQL Server. Start by Googling "sql server aggregate string concatenation".
This is s sample:
----- Test Data ----------
DECLARE #TestData TABLE (customer VARCHAR(10),product VARCHAR(10),quantity INT)
INSERT INTO #TestData
SELECT 'CLI01','A',10 UNION ALL
SELECT 'CLI01','B',20 UNION ALL
SELECT 'CLI02','A',31 UNION ALL
SELECT 'CLI03','A',10 UNION ALL
SELECT 'CLI03 ','C',12
----- Query -------------
SELECT customer,CASE WHEN COUNT( DISTINCT t.product)=1 THEN 'Only ' ELSE '' END + LEFT(c.product,LEN(c.product)-1) AS Product,SUM(quantity) AS quantity
FROM #TestData AS t
CROSS APPLY(SELECT a.product+'+' FROM #TestData AS a WHERE a.customer=t.customer FOR XML PATH('')) c(product)
GROUP BY customer,c.product
ORDER BY t.customer
customer Product quantity
CLI01 A+B 30
CLI02 Only A 31
CLI03 A+C 22
Have you tried using stuff? This will give you what you need. Works with as many products as necessary, from sql 2008 onwards.
CREATE TABLE x (customer VARCHAR (20), product CHAR(1), quantity INT )
INSERT INTO x
VALUES( 'CLI01', 'A', 10),
( 'CLI01', 'B', 20),
( 'CLI02', 'A', 31),
( 'CLI03', 'A', 10),
( 'CLI03', 'C', 12)
SELECT x1.customer, x3.Products, SUM(x1.quantity)
FROM x x1
CROSS APPLY ( SELECT Products = STUFF( (select '+' + product AS [text()]
FROM x x2
WHERE x2.customer = x1.customer
FOR XML PATH ('') ), 1, 1,'') ) x3
GROUP BY x1.customer, x3.Products

How to find max value from each group and display their information when using "group by"

For example, i create a table about people contribue to 2 campaigns
+-------------------------------------+
| ID Name Campaign Amount (USD) |
+-------------------------------------+
| 1 A 1 10 |
| 2 B 1 5 |
| 3 C 2 7 |
| 4 D 2 9 |
+-------------------------------------+
Task: For each campaign, find the person (Name, ID) who contribute the most to
Expected result is
+-----------------------------------------+
| Campaign Name ID |
+-----------------------------------------+
| 1 A 1 |
| 2 D 4 |
+-----------------------------------------+
I used "group by Campaign" but the result have 2 columns "Campagin" and "max value" when I need "Name" and "ID"
Thanks for your help.
Edited: I fix some values, really sorry
You can use analytic functions for this:
select name, id, amount
from (select t.*, max(amount) over (partition by campaign) as max_amount
from t
) t
where amount = max_amount;
You can also do it by giving a rank/row_number partiton by campaign and order by descending order of amount.
Query
;with cte as(
select [num] = dense_rank() over(
partition by [Campaign]
order by [Amount] desc
), *
from [your_table_name]
)
select [Campaign], [Name], [ID]
from cte
where [num] = 1;
Try the next query:-
SELECT Campaign , Name , ID
FROM (
SELECT Campaign , Name , ID , MAX (Amount)
FROM MyTable
GROUP BY Campaign , Name , ID
) temp;
Simply use Where Clause with the max of amount group by Campaign:-
As following generic code:-
select a, b , c
from tablename
where d in
(
select max(d)
from tablename
group by a
)
Demo:-
Create table #MyTable (ID int , Name char(1), Campaign int , Amount int)
go
insert into #MyTable values (1,'A',1,10)
insert into #MyTable values (2,'B',1,5)
insert into #MyTable values (3,'C',2,7)
insert into #MyTable values (4,'D',2,9)
go
select Campaign, Name , ID
from #MyTable
where Amount in
(
select max(Amount)
from #MyTable
group by Campaign
)
drop table #MyTable
Result:-
Please find the below code for the same
SELECT *
FROM #MyTable T
OUTER APPLY (
SELECT COUNT(1) record
FROM #MyTable T1
where t.Campaign = t1.Campaign
and t.amount < t1.amount
)E
where E.record = 0

SQL result into 3 bucket by count

=============================
Itemnumber| Check_ind| year
=============================
123 |Y | 2011
456 |Y | 2011
123 |Y | 2012
456 |Y | 2011
456 |Y | 2011
I want to result to be
=====================
1| 2-3| 4
=====================
123| 456 |
I want to count total time that each itemnumber appear in the table and where year=2011, then put it into bucket. my itnitial think was something like :
SELECT case when count(Itemnumber)>=0 and <=1 then '1'
case when count(Itemnumber)>=2 and <=3 then '2-3'
else '4' end
from table where year = '2011'
My guess is maybe there is a better solution using pivot.
While someone find that, here is my solution:
You could handle null with a case to show space if that is a problem.
Sql Fiddle Demo
I include a few more data in the sample, let me know if that is ok.
Have to use FULL JOIN because I don't know what group will have the most items.
.
with item_count AS (
SELECT itemnumber, count(*) as total
FROM item
WHERE year = '2011'
GROUP BY itemnumber
), t_01 AS (
SELECT itemnumber, ROW_NUMBER() OVER(ORDER BY itemnumber) AS row_id
FROM item_count
WHERE total between 0 and 1
), t_02 AS (
SELECT itemnumber, ROW_NUMBER() OVER(ORDER BY itemnumber) AS row_id
FROM item_count
WHERE total between 2 and 3
), t_03 AS (
SELECT itemnumber, ROW_NUMBER() OVER(ORDER BY itemnumber) AS row_id
FROM item_count
WHERE total = 4
)
SELECT t_01.itemnumber as '0-1', t_02.itemnumber as '2-3', t_03.itemnumber as '4'
from
t_01
full join t_02
on t_01.row_id = t_02.row_id
full join t_03
on t_01.row_id = t_03.row_id
I add item 789 and 999 to the data sample
I think this is what you need -
DECLARE #T TABLE ( ItemNumber VARCHAR(5)
,Check_Ind CHAR(1)
,YEAR varchar(4)
)
INSERT INTO #T VALUES ('123','Y','2011')
,('456','Y','2011')
,('123','Y','2012')
,('456','Y','2011')
,('456','Y','2011')
SELECT * FROM #t
SELECT CASE WHEN Count(ItemNumber) < 2 THEN ItemNumber ELSE '' END [0-1]
,CASE WHEN Count(ItemNumber) BETWEEN 2 AND 3 THEN ItemNumber ELSE '' END [2-3]
,CASE WHEN Count(ItemNumber) > 3 THEN ItemNumber ELSE '' END [4]
FROM #T
WHERE YEAR = '2011'
GROUP BY ItemNumber
This question is not well defined so I am somewhat making a guess here. Notice I also am posting ddl and sample data in a consumable format. This makes things a lot easier for the people trying to help.
create table #Something
(
Itemnumber int,
Check_ind char(1),
MyYear int
)
insert #Something
select 123, 'Y', 2011 union all
select 456, 'Y', 2011 union all
select 123, 'Y', 2012 union all
select 456, 'Y', 2011 union all
select 456, 'Y', 2011
--Now add another group for the "2-3" bucket
insert #Something
select 12, 'Y', 2011 union all
select 12, 'Y', 2011;
with GroupSubtotals as
(
select case when COUNT(ItemNumber) < 2 then 1 end as [0-1]
, case when COUNT(ItemNumber) > 1 and COUNT(ItemNumber) < 4 then 1 end as [2-3]
, case when COUNT(ItemNumber) > 3 then 1 end as [4]
from #Something s
where s.MyYear = 2011
group by ItemNumber
)
select SUM([0-1]) as [0-1]
, SUM([2-3]) as [2-3]
, SUM([4]) as [4]
from GroupSubtotals