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
Related
I have a table A that contains a series of unique id. I have an other table B that contains some of this id but not each one of them, a field called value and a another field called idcategory. In this table B, id can appears several times because of differents categories.
I want to list all my id in the table A in a unique way and the specific value associated in the table B in a defined categorie (idcategorie = 1). Id in the table A could not appear in the table B, but i want this information anyway in my final result and without duplication.
Here is an illustration :
Table A
id
-----
1
2
3
4
5
6
7
8
Table B
id | idcategory | value
------------------------
1 | 1 | red
1 | 2 | circle
2 | 1 | green
3 | 1 | blue
3 | 2 | square
4 | 1 | green
4 | 2 | circle
5 | 1 | red
5 | 2 | square
8 | 2 | circle
Result
id | idcategory | value
------------------------
1 | 1 | red
2 | 1 | green
3 | 1 | blue
4 | 1 | green
5 | 1 | red
6 | null | no value
7 | null | no value
8 | null | no value
What is the best way to achieve this in postgreSQL ? LEFT JOIN ? UNION ?
You seem to want a left join:
select a.id, b.idcategory, b.value
from a left join
b
on b.id = a.id and b.idcategory = 1;
The value column has NULL rather than 'no value'. You can replace it, but NULL usually serves that purpose.
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
I have a dataset in a SQL Server database, and I'd like to list run a query to list records from a table, multiple times according to the respective integer value in a column of that table.
I've seen many of the posts about listing a record multiple times in a query, with most of the more suitable responses describing a solution involving Cross join. The situation I'm describing is slightly different from what I've seen of yet, however.
The table structure of the records is akin to the following:
+-----+-------+------+------+
| id | name | type | num |
+-----+-------+------+------+
| 1 | bob | red | 1 |
+-----+-------+------+------+
| 2 | sam | blue | 3 |
+-----+-------+------+------+
| 3 | viv | green| 2 |
+-----+-------+------+------+
I'd like to display this in a query result as follows...
+-----+-------+------+
| id | name | type |
+-----+-------+------+
| 1 | bob | red |
+-----+-------+------+
| 2 | sam | blue |
+-----+-------+------+
| 2 | sam | blue |
+-----+-------+------+
| 2 | sam | blue |
+-----+-------+------+
| 3 | viv | green|
+-----+-------+------+
| 3 | viv | green|
+-----+-------+------+
... where each record appears multiple times according to the number it is listed in the original row. (bob * 1, sam * 3, viv * 2)
Is this possible via cross-join or any other method available?
Note: This does not need to be terribly efficient.
You can try to use recursive CTE
;with cte as (
SELECT id,name,type,1 startnum,num
FROM T
UNION ALL
SELECT id,name,type , startnum+1,num
FROM cte
WHERE startnum+1<=num
)
SELECT id,name,type
FROM cte
order by id
sqlfiddle
Another option is an ad-hoc tally/numbers table
Example
Select A.ID
,A.[Name]
,A.[Type]
From YourTable A
Cross Apply (Select Top (A.[num]) N=Row_Number() Over (Order By (Select NULL)) From master..spt_values n1) B
Or a simple JOIN
Select A.ID
,A.[Name]
,A.[Type]
From YourTable A
Join (Select Top (100) N=Row_Number() Over (Order By (Select NULL)) From master..spt_values n1) B
on B.N<=A.[Num]
Both would Return
ID Name Type
1 bob red
2 sam blue
2 sam blue
2 sam blue
3 viv green
3 viv green
Use a Stored procedure
create a temporary table variable
Write insert into temporary table in a while loop
while counter < max(num)
insert into table where counter <=num
Finally select from table .
EDITED:
(Thanks, #MatBailie!)
Using a recursive CTE on the num column to generate the required number of repetitions, and doing a left-join:
Given table so1 as follows:
id name type num
1 bob red 1
2 sam blue 3
3 viv green 2
4 jon grey 7
Try:
WITH RECURSIVE exp_num AS
(
SELECT id, num FROM so1
UNION
SELECT id, num - 1 FROM exp_num WHERE num > 1
)
SELECT s1.* FROM so1 s1
LEFT JOIN
(
SELECT id FROM exp_num
) s2
ON s1.id = s2.id
Output:
id name type
1 bob red
2 sam blue
2 sam blue
2 sam blue
3 viv green
3 viv green
4 jon grey
4 jon grey
4 jon grey
4 jon grey
4 jon grey
4 jon grey
4 jon grey
Below is an example of a table in our CRM, its not the way I'd have chosen to store this data but thats by the by, What would be the 'nice' way to count how many times each option was selected by each team?
asking here before i go headlong into a convoluted case statement :)
+----------+--------+---------+---------+---------+
| PersonID | Team | Option1 | Option2 | Option3 |
+----------+--------+---------+---------+---------+
| 1 | Blue | A | B | C |
| 2 | Blue | B | C | D |
| 3 | Blue | D | A | E |
| 4 | Red | A | B | D |
| 5 | Red | B | A | C |
| 6 | Yellow | A | B | C |
| 7 | Yellow | A | C | D |
+----------+--------+---------+---------+---------+
Thanks in advance
You can unpivot your 3 option columns into a single column using CROSS APPLY and a table value constructor and then perform your count:
SELECT t.Team, upvt.[Option], COUNT(*) AS Occurances
FROM dbo.T
CROSS APPLY (VALUES (t.Option1), (t.Option2), (t.Option3)) AS upvt ([Option])
GROUP BY t.Team, upvt.[Option]
ORDER BY t.Team, upvt.[Option];
So this would give:
Team Option Occurances
-------------------------------
Blue A 2
Blue B 2
Blue C 2
Blue D 2
Blue E 1
Red A 2
Red B 2
Red C 1
Red D 1
Yellow A 2
Yellow B 1
Yellow C 2
Yellow D 1
You can use UNION ALL to move the values into single column and then, do aggregation:
select
team, option, count(*) cnt
from(
select team, option1 option from t union all
select team, option2 from t union all
select team, option3 from t
)t group by team, option;
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,