Check box like functionality in table using query in SQL Server - sql

I have a primary table with multiple records for the name but different quantity and colors.
TblPrimary: current table
id | name | color | Quan |
===+=========+=======+=======+
1 | Apple | Red | 10 |
2 | Banana | Yellow| 5 |
3 | Mango | Yellow| 8 |
4 | Apple | Green | 20 |
5 | Banana | Brown | 15 |
6 | Mango | Orange| 12 |
7 | Mango | Green | 5 |
This is my main table and I basically want data from the primary table like this. So basically the Quan in main table is sum of all individual Quan from the primary table. The colors (Red,Yellow,Brown) in the main table; are bits which indicate whether that color is present for the fruit or not in the primary table.
TblMain: new expected table
id | Name | Quan | Red | Yellow | Brown | Green | Orange |
===+=========+=======+=====+========+=======+=======+========+
1 | Apple | 30 | 1 | 0 | 0 | 1 | 0 |
2 | Banana | 20 | 0 | 1 | 1 | 0 | 0 |
3 | Mango | 25 | 0 | 1 | 0 | 1 | 1 |
I have got the below query and I have group by and the sum. I am not able to get the colors portion of the main table populated.
INSERT INTO TblMain(Name, Quan)
(SELECT Name, SUM(Quan)
FROM TblPrimary
GROUP BY Name)

You can try to use sum windows function in subquery then use condition aggregate function make your expect result.
select ROW_NUMBER() OVER(ORDER BY name) id,
name,
Quan,
SUM(CASE WHEN color = 'Red' THEN 1 ELSE 0 END) 'Red',
SUM(CASE WHEN color = 'Yellow' THEN 1 ELSE 0 END) 'Yellow',
SUM(CASE WHEN color = 'Brown' THEN 1 ELSE 0 END) 'Brown',
SUM(CASE WHEN color = 'Green' THEN 1 ELSE 0 END) 'Green',
SUM(CASE WHEN color = 'Orange' THEN 1 ELSE 0 END) 'Orange'
from (
SELECT name,
color,
SUM(Quan) OVER(PARTITION BY name ORDER BY name) Quan
FROM TblPrimary
) t1
group by name,Quan
sqlfiddle
Result
id name Quan Red Yellow Brown Green Orange
1 Apple 30 1 0 0 1 0
2 Banana 20 0 1 1 0 0
3 Mango 25 0 1 0 1 1

You can use PIVOT to convert values to columns. In your case this becomes a bit cumbersome, because you want the row sum as well as well as the 0/1 flags
SELECT
name,
ISNULL(Red,0) + ISNULL(Yellow,0) + ISNULL(Brown,0) + ISNULL(Green,0) + ISNULL(Orange,0) As Quan,
CASE WHEN Red IS NULL THEN 0 ELSE 1 END AS Red,
CASE WHEN Yellow IS NULL THEN 0 ELSE 1 END AS Yellow,
CASE WHEN Brown IS NULL THEN 0 ELSE 1 END AS Brown,
CASE WHEN Green IS NULL THEN 0 ELSE 1 END AS Green,
CASE WHEN Orange IS NULL THEN 0 ELSE 1 END AS Orange
FROM
(SELECT name, color, Quan
FROM dbo.TblPrimary) AS SourceTable
PIVOT
(
SUM(Quan)
FOR color IN (Red, Yellow, Brown, Green, Orange)
) AS PivotTable;
If you display the sums directly for the colors instead of the flags, the query simplifies to:
SELECT
name, Red, Yellow, Brown, Green, Orange
FROM
(SELECT name, color, Quan
FROM dbo.TblPrimary) AS SourceTable
PIVOT
(
SUM(Quan)
FOR color IN (Red, Yellow, Brown, Green, Orange)
) AS PivotTable;
And the result will be:
name Red Yellow Brown Green Orange
==========================================
Apple 10 null null 20 null
Banana null 5 15 null null
Mango null 8 null 5 12

Related

Count percentage of certain column values and add it to a new column

I have a following table
[Table_OrangeIsNewBlack]
ID | Name | Color | RedPercent
------------------------------------------
1 | Donald | Orange |
2 | Hillary | White |
3 | Barack | Black |
4 | Bernie | Grey |
1 | Donald | Red |
2 | Hillary | Red |
3 | Barack | Black |
4 | Bernie | Grey |
1 | Donald | Red |
2 | Hillary | Blue |
3 | Barack | Red |
4 | Bernie | Purple |
I need to add a percentage value presenting how often person is 'Red'
Donald's record
1 Donald Orange
1 Donald Red
1 Donald Red
RedPercent : (2 / 3 ) * 100 = 66,66
Hillary
2 Hillary White
2 Hillary Red
2 Hillary Blue
'Blue' and 'Purple' do not count, thus :
RedPercent : (1 / 2) * 100 = 50,00
Barack
3 Barack Black
3 Barack Black
3 Barack Red
RedPercent : (1 / 3) * 100 = 33,33
Bernie
4 Bernie Grey
4 Bernie Blue
4 Bernie Purple
'Blue' and 'Purple' do not count
RedPercent : (0 / 1) * 100 = 00,00
Result needed:
[Table_OrangeIsNewBlack]
ID | Name | Color | RedPercent
------------------------------------------
1 | Donald | Orange | 66,66
2 | Hillary | White | 50,00
3 | Barack | Black | 33,33
4 | Bernie | Grey | 00,00
1 | Donald | Red | 66,66
2 | Hillary | Red | 50,00
3 | Barack | Black | 33,33
4 | Bernie | Grey | 00,00
1 | Donald | Red | 66,66
2 | Hillary | Blue | 50,00
3 | Barack | Red | 33,33
4 | Bernie | Purple | 00,00
I've tried :
;WITH CTE AS
(
SELECT ID,
-- Division: count of Reds / count of persons all rows
((SELECT COUNT(ID) FROM Table_OrangeIsNewBlack
WHERE Color = 'Red' )
/
(SELECT COUNT(ID) FROM Table_OrangeIsNewBlack
WHERE ( Color <> 'Blue' and
Color <> 'Purple' ))
)
AS redprcnt
-- end of Division
FROM Table_OrangeIsNewBlack
)
UPDATE T1
SET RedPercent = CTE.redprcnt
FROM Table_OrangeIsNewBlack T1
INNER JOIN CTE ON(T1.ID = CTE.ID)
SELECT clause...
SELECT ID,
-- Division: count of Reds / count of persons all rows
((SELECT COUNT(ID) FROM Table_OrangeIsNewBlack
WHERE Color = 'Red' )
/
(SELECT COUNT(ID) FROM Table_OrangeIsNewBlack
WHERE ( Color <> 'Blue' and
Color <> 'Purple' ))
)
AS redprcnt
-- end of Division
FROM Table_OrangeIsNewBlack
...gives only zeros for redprcnt
Should i use GROUP BY at some point?
Any suggestions?
Thanks in advance.
COUNT usually results in a INTEGER, thus COUNT/COUNT is an integer division which truncates and your base query tries to calculate the overall percentage instead of the % per group.
Additionally your calculation can be simplified using conditional aggregation:
SELECT ID,
-- Division: count of Reds / count of persons all rows
100.00 * COUNT(CASE WHEN Color = 'Red' THEN ID END)
/ NULLIF(COUNT(CASE WHEN Color <> 'Blue' and Color <> 'Purple' THEN ID END), 0) AS redprcnt
FROM Table_OrangeIsNewBlack
GROUP BY ID
The CASE returns NULL for non-matching rows which are not counted.
Edit:
If you actually need to repeat the percentage for each row you can switch to a Windowed Aggregate instead:
SELECT ID, Name, Color,
100.00 * Count(CASE WHEN Color = 'Red' THEN ID END) Over(PARTITION BY NAME)
/ NULLIF(Count(CASE WHEN Color <> 'Blue' AND Color <> 'Purple' THEN ID END) Over(PARTITION BY NAME), 0) AS redprcnt
FROM Table_OrangeIsNewBlack
Interesting Task, I have made script that may help you
;WITH CTE AS
(
SELECT * ,COUNT(CASE WHEN Color = 'Red' THEN ID END) OVER(Partition by Name,Color) ColorCount,
COUNT(CASE WHEN Color <> 'Blue' and Color <> 'Purple' THEN ID END) OVER(Partition by Name)TotalCount
FROM TableName
)
SELECT ID,Name, (SELECT MAX(((ColorCount*1.0)/(CASE WHEN TotalCount = 0 THEN 1 ELSE TotalCount END))*100)
FROM CTE InnerCte
WHERE OuterCTE.ID=InnerCte.ID AND OuterCTE.Name=InnerCte.Name )
FROM CTE OuterCTE

Transforming multiple rows into columns w. single row

I need some help figuring out how best to transform an an array into a row-vector. My array looks like this:
+-----+-------+----------+
| ID | Grade | Quantity |
+-----+-------+----------+
| Ape | Water | Y |
| Ape | Juice | Y |
| Ape | Milk | Y |
+-----+-------+----------+
Each ID can have up to 4 rows distinguished only by grade (Water, Juice, Beer, Milk); the list of possible values is static.
My desired output is this:
+-----+----------+-------+-------+------+------+
| ID | Quantity | Water | Juice | Beer | Milk |
+-----+----------+-------+-------+------+------+
| Ape | Y | 1 | 1 | 0 | 1 |
+-----+----------+-------+-------+------+------+
My own efforts have carried me as far as the PIVOT-operator, which transforms Grade-values into columns, but it doesn't group the rows by ID, leaving me with an equal number of rows post-transformation.
SELECT ID, Quantity, Water, Juice, Beer, Milk
FROM
(SELECT ID, Grade, Quantity FROM Feeding WHERE ID = 'Ape') src
PIVOT(
COUNT(Quantity) FOR [Grade] IN (ID, Quantity, Water, Juice, Beer, Milk)
)AS TransformData
Output:
+-----+----------+-------+-------+------+------+
| ID | Quantity | Water | Juice | Beer | Milk |
+-----+----------+-------+-------+------+------+
| Ape | Y | 1 | 0 | 0 | 0 |
| Ape | Y | 0 | 1 | 0 | 0 |
| Ape | Y | 0 | 0 | 0 | 1 |
+-----+----------+-------+-------+------+------+
Any suggestions?
How about;
;WITH Feeding(id,grade,quantity) as (
select 'Ape','Water','Y' union all
select 'Ape','Juice','Y' union all
select 'Ape','Juice','Y' union all
select 'Ape','Juice','Y' union all
select 'Ape','Juice','Y' union all
select 'Ape','Milk', 'N'
)
SELECT * FROM
(SELECT ID, Grade, Quantity agg, Quantity FROM Feeding WHERE ID = 'Ape') src
PIVOT ( COUNT(agg) FOR [Grade] IN (Water, Juice, Beer, Milk) ) AS TransformData
--
ID Quantity Water Juice Beer Milk
Ape N 0 0 0 1
Ape Y 1 4 0 0
you can try following query:-
SELECT ID, Quantity, CASE WHEN Grade = 'WATER' THEN 1 ELSE 0 END AS WATER,
CASE WHEN Grade = 'JUICE' THEN 1 ELSE 0 END AS JUICE,
CASE WHEN Grade = 'BEER' THEN 1 ELSE 0 END AS BEER,
CASE WHEN Grade = 'MILK' THEN 1 ELSE 0 END AS MILK
FROM YOUR_TABLE;
select id, quantity,
case when grade = 'Water' then 1 else 0 end as Water,
when grade = 'Juice' then 1 else 0 end as Juice,
when grade = 'Milk' then 1 else 0 end as Milk,
when grade = 'Beer' then 1 else 0 end as Beer
from feeding
As the value list is static, this is a way to do it.
Try this
SELECT id
,quantity
,CASE
WHEN grade = 'Water'
THEN 1
ELSE 0
END AS Water
,when grade = 'Juice' then 1 ELSE 0 END AS Juice
,when grade = 'Milk' then 1 ELSE 0 END AS Milk
,when grade = 'Beer' then 1 ELSE 0 END AS Beer
FROM feeding
Try this to get single row result
SELECT Id,Quantity,
SUM(CASE WHEN Grade='Water' THEN 1 ELSE 0 END) AS Water,
SUM(CASE WHEN Grade='Juice ' THEN 1 ELSE 0 END) AS Juice ,
SUM(CASE WHEN Grade='Beer' THEN 1 ELSE 0 END) AS Beer,
SUM(CASE WHEN Grade='Milk' THEN 1 ELSE 0 END) AS Milk
FROM Feed F
GROUP BY Id,Quantity
SQL Fiddle Demo

SQL Count: How to count value of a column from the same value of other column

Sorry about the title.
Sample:
I have this table(tblTry):
id | Name | Color
____________________
1 | XYZ | Black
2 | XYZ | Black
3 | ASD | Red
4 | ASD | White
5 | ASD | White
And this is the output I want:
Name | Black | Red | White
__________________________
XYZ | 2 | 0 | 0
ASD | 0 | 1 | 2
I have this sql but it gives me different output:
select distinct
Name,
(select count(*) from tblTry where Color= 'Black') as Black,
(select count(*) from tblTry where Color= 'Red') as Red,
(select count(*) from tblTry where Color= 'White') as White,
from tblTry
group by Name
sql above output:
__________________________
Name | Black | Red | White
__________________________
XYZ | 2 | 1 | 2
ASD | 2 | 1 | 2
Can anyone help me?
Thanks
This is a pivot, which has several solutions. One that is general across databases is to use conditional aggregation:
select name,
sum(case when Color = 'Black' then 1 else 0 end) as Black,
sum(case when Color = 'Red' then 1 else 0 end) as Red,
sum(case when Color = 'White' then 1 else 0 end) as White
from tblTry
group by name;
The problem with your query is that the counts need to be correlated to each row. You would do this with an additional where condition:
(select count(*) from tblTry t2 where t2.Color= 'Black' and t2.name = tblTry.name) as Black,

Transpose Microsoft SQL Server table [duplicate]

This question already has answers here:
Simple way to transpose columns and rows in SQL?
(9 answers)
Closed 8 years ago.
I have two tables, a header table and a detail table. The data looks like this:
OBJ_NO | Name
12345 | Fred
67891 | Bob
Detail table:
OBJ_NO | HEADER_OBJ_NO | CODE
1 | 12345 | Red
2 | 12345 | Blue
3 | 12345 | Green
4 | 67891 | Red
5 | 67781 | Green
Essentially what I am after is to see something like this:
OBJ_NO | Name | Red | Blue | Green
12345 | Fred | 1 | 1 | 1
67891 | Bob | 1 | 0 | 1
It could be different number of "Colours" as well. Its not set. And not each Header Ref would have one of each colour as shown in the example above.
How would I achieve this?
A SQL Server PIVOT will work for you.
WITH CTE(OBJ_NO, NAME, RED, BLUE, GREEN)
AS
(
SELECT [OBJ_NO]
,[NAME]
,[red],[blue],[green]
FROM (SELECT T2.[OBJ_NO]
,T2.[NAME], T1.CODE FROM [dbo].[Table_2] T2
INNER JOIN [dbo].[Table_1] T1 ON T1.HEADER_OBJ_NO = T2.OBJ_NO ) AS SourceTable
PIVOT
(MAX(CODE)
FOR CODE IN ([red], [blue], [green])
)AS PivotTable
)
SELECT OBJ_NO
,NAME
,RED = CASE WHEN RED = 'red' THEN 1 ELSE 0 END
,BLUE = CASE WHEN BLUE = 'blue' THEN 1 ELSE 0 END
,GREEN = CASE WHEN GREEN = 'green' THEN 1 ELSE 0 END
FROM CTE

SQL join rows into one

| fk | red | brown | green |
|1337| 1 | 0 | 0 |
|1337| 0 | 1 | 0 |
|1337| 0 | 0 | 1 |
In tSql how would i return these as one row to say that 1337 has been red, brown and green at some point?
| fk | red | brown | green |
|1337| 1 | 1 | 1 |
Using Max function Grouping by fk
select fk, max(red) red, max(brown) brown, max(green) green
from yourTable
group by fk
If red, brown & green are bit type fields then try as below
select fk, max(red+0) red, max(brown+0) brown, max(green+0) green
from yourTable
group by fk
By grouping :
Select fk ,
Max(red) as red,
Max(brown) as brown,
Max(green) as green
FROM myTableName
GROUP BY fk
Like so:
SELECT fk
,MAX(red)
,MAX(brown)
,MAX(green)
FROM yourtable
GROUP BY fk