SQL query create cross column - sql

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

Related

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 |

Transpose SQL table results using Pivot

I am trying to transpose column results in my table into row results. Here is the query that generates the table results:
CREATE TABLE Zone
([Zone] varchar(9), [CompanyID] int, [SubCount] int);
CREATE TABLE Company
([UniqueIdentifier]int, [Name] varchar(50));
--Adding Values into the table
INSERT INTO Company
([UniqueIdentifier], [Name])
VALUES
( 1, 'CompanyA'),
( 2, 'CompanyB'),
( 3, 'CompanyC'),
( 4, 'CompanyD'),
( 5, 'CompanyE');
--Adding Values to the table
INSERT INTO Zone
([Zone], [CompanyID], [SubCount])
VALUES
( 'Zone1', 1, 100),
( 'Zone2', 1, 200),
( 'Zone3', 2, 1250),
( 'Zone4', 3, 1440),
( 'Zone5', 4, 1445),
( 'Zone6', 4, 3250),
( 'Zone7', 5, 4440);
--Getting TOTALS
SELECT
CASE WHEN GROUPING(dbo.Company.Name)=1 THEN 'Grand Total' else dbo.Company.Name end as Company,
SUM(dbo.Zone.SubCount) as Subs
FROM dbo.Company INNER JOIN dbo.Zone ON dbo.Company.UniqueIdentifier = dbo.Zone.CompanyID
WHERE (dbo.Zone.SubCount IS NOT NULL) AND (dbo.Zone.SubCount > 0)
Group by ROLLUP(dbo.Company.Name)
ORDER BY Subs DESC;
HERE ARE THE RESULTS OF THE QUERY:
Company | Subs
------------------------
1 Grand Total | 12125
2 CompanyD | 4695
3 CompanyE | 4440
4 CompanyC | 1440
5 CompanyB | 1250
6 CompanyA | 300
DESIRED RESULT WOULD LOOK LIKE:
Company |CompanyA|CompanyB|CompanyC|CompanyE|CompanyD|Grand Total
---------------------------------------------------------------------
Subs | 300 | 1250 | 1440 | 4440 | 4695 | 12125
ANY HELP IS GREATLY APPRECIATED!
All you need to do is use pivot function for converting these rows into columns
SELECT 'Subs' AS Company
,[CompanyA]
,[CompanyB]
,[CompanyC]
,[CompanyD]
,[CompanyE]
,[Grand Total]
FROM (
SELECT CASE
WHEN GROUPING(dbo.Company.NAME) = 1
THEN 'Grand Total'
ELSE dbo.Company.NAME
END AS Company
,SUM(dbo.Zone.SubCount) AS Subs
FROM dbo.Company
INNER JOIN dbo.Zone ON dbo.Company.UNIQUEIDENTIFIER = dbo.Zone.CompanyID
WHERE (dbo.Zone.SubCount IS NOT NULL)
AND (dbo.Zone.SubCount > 0)
GROUP BY ROLLUP(dbo.Company.NAME)
) a
pivot(max(subs) FOR Company IN (
[CompanyA]
,[CompanyB]
,[CompanyC]
,[CompanyD]
,[CompanyE]
,[Grand Total]
)) piv;

GROUP BY with summing null values to every group

I want to group values and sum them up by a category value - or none.
For example, I have the following table:
+-------+----------+
| value | category |
+-------+----------+
| 99 | A |
| 42 | A |
| 76 | [NULL] |
| 66 | B |
| 10 | C |
| 13 | [NULL] |
| 27 | C |
+-------+----------+
My desired result should look like this:
+-------+----------+
| sum | category |
+-------+----------+
| 230 | A |
| 155 | B |
| 126 | C |
| 89 | [NULL] |
+-------+----------+
I tried a group by category but obviously this doesn't bring up the right numbers.
Any ideas?
I'm using SQL Server 2012.
EDIT:
Ok, as requested, I can explain my intents and give my query so far, although that is not very helpful I think.
I need to sum all value for the given categories AND add the sum of all values without a category [=> NULL]
So in my example, I would sum
99 + 42 + 76 + 13 = 230 for category A
66 + 76 + 13 = 155 for category B
10 + 27 + 76 + 13 = 126 for category C
76 + 13 = 89 for no category
I hope that gives you an idea of my goal.
Query so far:
SELECT SUM([value]), [category]
FROM [mytable]
GROUP BY [category]
First calculate the sum of nulls then add it to each group:
DECLARE #t TABLE
(
value INT ,
category CHAR(1)
)
INSERT INTO #t
VALUES ( 99, 'A' ),
( 42, 'A' ),
( 76, NULL ),
( 66, 'B' ),
( 10, 'C' ),
( 13, NULL ),
( 27, 'C' )
;with cte as(select sum(value) as s from #t where category is null)
select category, sum(value) + s
from #t
cross join cte
where category is not null
group by category, s
Another version:
;WITH cte AS(SELECT category, SUM(value) OVER(PARTITION BY category) +
SUM(CASE WHEN category IS NULL THEN value ELSE 0 END) OVER() AS value
FROM #t)
SELECT DISTINCT * FROM cte WHERE category IS NOT NULL
If you want to add the NULL values to all the groups, then do something like:
with cte as (
select category, sum(value) as sumv
from t
group by category
)
select cte.category,
(cte.sumv +
(case when category is not null then coalesce(ctenull.sumv) else 0 end)
) as sumWithNulls
from cte left join
cte ctenull
on ctenull.category is null -- or should that be `= '[NULL]'`?
This seems like a strange operation.
EDIT:
You can almost do this with window functions:
select category,
(sum(value) +
sum(case when category is null then sum(value) else 0 end) over ()
) as sumWithNulls
from t
group by category;
The problem is that NULLs get over counted for that category. So:
select category,
(sum(value) +
(case when category is not null
then sum(case when category is null then sum(value) else 0 end) over ()
else 0
end
) as sumWithNulls
from t
group by category;
You want to get the sum of the NULL category and add it to the value of the other (non-null) categories:
DECLARE #Table1 TABLE (Value int, Category varchar(1))
DECLARE #NullCategorySum int
INSERT INTO #Table1
(Value, Category)
VALUES
(99, 'A'),
(42, 'A'),
(76, NULL),
(66, 'B'),
(10, 'C'),
(13, NULL),
(27, 'C')
SELECT #NullCategorySum = SUM(Value)
FROM #Table1
WHERE Category IS NULL
SELECT SUM(t1.Value)
+ CASE
WHEN Category IS NOT NULL THEN #NullCategorySum
END
AS SumValue, Category
FROM #Table1 t1
GROUP BY Category
This outputs
SumValue Category
89 NULL
230 A
155 B
126 C
Maybe you just missed using the SUM built-in function? This should work:
SELECT
SUM(value) AS [sum], category
FROM
[YourTableHere]
GROUP BY category
Edit: Ah, I see what you are doing now. I was able to do it by joining a 2nd query with just the NULL sum, so the NULL sum comes back with every row. Then you can just add it in the final step.
SELECT
MainSet.sum + JustNulls.sum AS [sum], MainSet.category
FROM
(SELECT SUM(X.value) AS [sum], X.category FROM [YourTableHere] X
WHERE X.category IS NOT NULL GROUP BY category
UNION SELECT 0, NULL) MainSet
FULL JOIN
(SELECT SUM(Y.value) AS [sum], Y.category FROM [YourTableHere] Y
WHERE Y.category IS NULL GROUP BY category) JustNulls ON 1=1
similar to levelonehuman put might be a little faster
declare #countNull int = (select sum(textUniqueWordCount) from docSVsys where mimeType is null);
select mimeType, sum(isnull(textUniqueWordCount, 0)) + #countNull as [sum]
from docSVsys
where mimeType is not null
group by mimeType
union
select null, #countNull;

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

SQL Server Rows to Multi-Columns

I have the following table and data:
CREATE TABLE SourceTbl ([Code] varchar(3), [Total] decimal, [Date] datetime );
INSERT INTO SourceTbl ([Code], [Total], [Date])
VALUES ('AA', 100, '2012-12-01'), ('AA', 200, '2013-02-01'), ('BB', 50, '2012-01-01');
A simple select will return
Code | Total | Date
'AA' | 100 | 2012-12-01
'AA' | 200 | 2013-02-01
'BB' | 50 | 2012-01-01
but what I need is the following
Code | Total | Date | Total | Date
'AA | 200 | 2013-02-01 | 100 | 2012-12-01
'BB | 50 | 2012-01-01 | null | null
I have been trying to do this using a PIVOT operator but without success (based on the question SQL Server Pivot multiple columns based on one column).
Using that example, all I get are two rows with null values.
The Total/Date columns can be repeated 13 times and they must be ordered by Date DESC.
SQL Fiddle: http://sqlfiddle.com/#!3/f37a1/2
Any help is appreciated!
Thanks!
If you need just two columns:
with cte as (
select *, row_number() over(partition by Code order by Date) as rn
from SourceTbl
)
select
code,
max(case when rn = 1 then Total end) as Total1,
max(case when rn = 1 then Date end) as Date1,
max(case when rn = 2 then Total end) as Total2,
max(case when rn = 2 then Date end) as Date2
from cte
group by code
=> sql fiddle demo
dynamic solution:
declare #stmt nvarchar(max)
;with cte as (
select distinct
cast(row_number() over(partition by Code order by Date) as nvarchar(max)) as rn
from SourceTbl
)
select #stmt = isnull(#stmt + ', ', '') +
'max(case when rn = ' + rn + ' then Total end) as Total' + rn + ',' +
'max(case when rn = ' + rn + ' then Date end) as Date' + rn
from cte
order by rn
select #stmt = '
with cte as (
select *, row_number() over(partition by Code order by Date) as rn
from SourceTbl
)
select
code, ' + #stmt + ' from cte group by code'
exec sp_executesql
#stmt = #stmt
=> sql fiddle demo
Are you trying to dynamically create columns in your result set?
If you had a third record of 'AA' with a total of 300 and the date of 03/01/2013 would you that mean you would want something like this displayed?
Code | Total | Date | Total | Date | Total | Date
AA | 200 | 2013-02-01 | 100 | 2012-12-01| 300 | 03-01-13
BB | 50 | 2012-01-01 | null | null | null | null