I'm trying to create a dynamic pivot code, where some columns are pivoted while others are not. Currently, I have one row per parent-child combination. I want a table where it is one row for every parent, with columns for child1, child2, etc. Some parents have multiple children, so number of columns(children) per parent is dynamic. I already have a column ordering the children by birthdate, and I'm adding this "childnumber" to the child columns.
Here's the sample table I'm starting with:
http://sqlfiddle.com/#!18/990f6/2
+===============+===========+-=============+=============+================+=============+==============+
| parent | parentage | parentgender | childname | childbirthdate | childgender | childnumber |
+===============+===========+==============+=============+================+=============+==============+
| John Smith | 32 | M | Jane Smith | 2005-05-21 | F | 1 |
| John Smith | 32 | M | Billy Smith | 2010-01-01 | M | 2 |
| Katherine Doe | 40 | F | Drew Fine | 2015-08-09 | M | 1 |
| Paula Lee | 28 | F | Peter Lee | 2009-12-30 | M | 1 |
| Paula Lee | 28 | F | Tim Lee | 2013-10-15 | M | 2 |
| Paula Lee | 28 | F | Andrew Lee | 2014-06-27 | M | 3 |
+---------------+-----------+--------------+-------------+----------------+-------------+--------------+
The final result should be:
+===============+===========+==============+============+=================+==============+=============+=================+==============+============+=================+===============+
| parent | parentage | parentgender | childname1 | childbirthdate1 | childgender1 | childname2 | childbirthdate2 | childgender2 | childname3 | childbirthdate3 | childgender3 |
+===============+===========+==============+============+=================+==============+=============+=================+==============+============+=================+===============+
| John Smith | 32 | M | Jane Smith | 2005-05-21 | F | Billy Smith | 2010-01-01 | M | null | null | null |
| Katherine Doe | 40 | F | Drew Fine | 2015-08-09 | M | null | null | null | null | null | null |
| Paula Lee | 28 | F | Peter Lee | 2009-12-30 | M | Tim Lee | 2013-10-15 | M | Andrew Lee | 2014-06-27 | M |
+---------------+-----------+--------------+------------+-----------------+--------------+-------------+-----------------+--------------+------------+-----------------+---------------+
My SQL Server Code Attempt:
IF OBJECT_ID('finaltable', 'U') IS NOT NULL DROP TABLE finaltable
DECLARE #columns AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SELECT #columns = STUFF((SELECT DISTINCT ',' + QUOTENAME(col + '_' + CAST(childnumber as varchar(50)))
FROM
families
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 1,'')
SET #query = 'SELECT parent, ' + #columns + '
FROM (
SELECT parent, parentage, parentgender, num = col+''_''+ CAST(childnumber as varchar(50))
FROM (
SELECT parent, childnumber, childname, childbirthdate, childgender
FROM families
) AS x
) AS source
PIVOT
(
MAX(childnumber)
FOR num in (' + #columns + ')
) AS pvt '
execute(#query);
I can't get this query to execute, and I'm not sure if my issue is with defining 'col' or if there are additional problems such as needing to unpivot first. Any help is greatly appreciated.
Related
I'm having a table called table such that:
| id | name | city |
|----|-------|---------|
| 0 | Rose | Madrid |
| 1 | Alex | Lima |
| 2 | Rose | Sidney |
| 3 | Mario | Glasgow |
And I need to UPDATE the table so that two rows sharing the same name combined into a new one and deleted.
| id | name | city |
|----|-------|----------------|
| 1 | Alex | Lima |
| 3 | Mario | Glasgow |
| 4 | Rose | Madrid, Sidney |
I don't care if it has to be done in several SQL statements.
So far all I've done is to list the rows that are affected by this.
SELECT *
FROM table
WHERE name IN (
SELECT name
FROM table
GROUP BY name
HAVING COUNT(*) > 1
);
Assuming that id is auto increment primary key, you need an INSERT and a DELETE statement:
insert into tablename(name, city)
select name, group_concat(city, ',')
from tablename
group by name
having count(*) > 1;
delete from tablename
where instr(name, ',') = 0
and exists (
select 1 from tablename t
where t.id <> tablename.id and t.name = tablename.name
and ',' || t.city || ',' like '%,' || tablename.city || ',%'
);
See the demo.
Results:
| id | name | city |
| --- | ----- | ------------- |
| 1 | Alex | Lima |
| 3 | Mario | Glasgow |
| 4 | Rose | Madrid,Sidney |
1) Suppose i have a table like this:-
| id | color_code | fruit |
|:------|--------------|----------------:|
| 1 | 000001 | apple |
| 2 | 000001 | apple |
| 3 | 000001 | apple |
| 4 | 000002 | lemon |
| 5 | 000002 | lemon |
| 6 | 000003 | grapes |
| 7 | 000003 | grapes |
How can i group by the fruit column according to the color_code column in sql server?
like this i suppose:-
| id | color_code | fruit | group_concat(id) |
|:------|--------------|-----------------|---------------------|
| 1 | 000001 | apple | 1,2,3 |
| 4 | 000002 | lemon | 2,5 |
| 6 | 000003 | grapes | 6,7 |
2) What if i have 3 tables (like shown below) which require join, how can i achieve this?
series_no table:
| id | desc_seriesno |
|:------|----------------:|
| 7040 | AU1011 |
| 7041 | AU1022 |
| 7042 | AU1033 |
| 7043 | AU1044 |
| 7044 | AU1055 |
| 7045 | AU1066 |
brand table:
| id | desc_brand |
|:------|----------------:|
| 1020 | Audi |
| 1021 | Bentley |
| 1022 | Ford |
| 1023 | BMW |
| 1024 | Mazda |
| 1025 | Toyota |
car_info table:
| seriesno_id | brand_id | color |
|:---------------|------------|--------:|
| 7040 | 1020 | white |
| 7040 | 1020 | black |
| 7040 | 1020 | pink |
| 7041 | 1021 | yellow |
| 7041 | 1021 | brown |
| 7042 | 1022 | purple |
| 7042 | 1022 | black |
| 7042 | 1022 | green |
| 7043 | 1023 | blue |
| 7044 | 1024 | red |
| 7045 | 1025 | maroon |
| 7045 | 1025 | white |
this is my current query with sql server 2014:-
SELECT SN.id AS seriesid, B.id AS brandid, B.desc_brand
FROM [db1].[dbo].[series_no] SN
LEFT JOIN [db1].[dbo].[car_info] CI
ON CI.seriesno_id = SN.id
RIGHT JOIN [db1].[dbo].[brand] B
ON B.id = CI.brand_id
GROUP BY SN.id, B.id
ORDER BY SN.id ASC
but unfortunately it gave me an error since i cannot group by similar string this way.
i want it to be like this:-
| seriesid | brandid | desc_brand | count |
|:-----------|------------|---------------|-------|
| 7040 | 1020 | Audi | 3 |
| 7041 | 1021 | Bentley | 2 |
| 7042 | 1022 | Ford | 3 |
| 7043 | 1023 | BMW | 1 |
| 7044 | 1024 | Mazda | 1 |
| 7045 | 1025 | Toyota | 2 |
1 Fruit Color
Assuming the table name is FruitColor, you can get the desired output by the following query -
SELECT MIN(id) AS id
, color_code
, fruit
, group_concat_id = STUFF((SELECT ',' + CAST(id AS VARCHAR)
FROM FruitColor AS fci
WHERE fci.fruit = fc.fruit AND fci.color_code = fc.color_code
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM FruitColor AS fc
GROUP BY color_code, fruit
ORDER BY id;
The MIN() selects the first id of the group.
Since there is no default GROUP_CONCAT function like in MySql in SQL Server, you have to use the STUFF function and FOR XML PATH. To learn more about group concat you can visit this link https://sqlperformance.com/2014/08/t-sql-queries/sql-server-grouped-concatenation
You can customize the WHERE clause to match only by color_code.
2. You can have several options for this -
Option (a): Show counts for series with brands
SELECT seriesno_id AS seriesid, ci.brand_id AS bandid, desc_brand, COUNT(*) AS [count]
FROM db1.dbo.car_info AS ci
LEFT JOIN db1.dbo.brand AS b ON (b.id = ci.brand_id)
GROUP BY seriesno_id, ci.brand_id, desc_brand;
Here you don't need to use the series table if you want to show counts for cars having brand(s).
You may not need to use the RIGHT JOIN on the brand table because if brand table contains a record which
is not in car_info table, then seriesno_id would be null.
Option (b): Show counts for all the series with or without a brand
SELECT sn.id AS seriesid, ci.brand_id AS bandid, desc_brand, COUNT(*) AS [count]
FROM db1.dbo.series_no AS sn
LEFT JOIN db1.dbo.car_info AS ci ON (ci.seriesno_id = sn.id)
LEFT JOIN db1.dbo.brand AS b ON (b.id = ci.brand_id)
GROUP BY sn.id, ci.brand_id, desc_brand;
Option (c): The work around for selecting a column which is not in a GROUP BY
SELECT seriesno_id AS seriesid, ci.brand_id AS bandid, MAX(desc_brand) AS desc_brand, COUNT(*) AS [count]
FROM db1.dbo.car_info AS ci
LEFT JOIN db1.dbo.brand AS b ON (b.id = ci.brand_id)
GROUP BY seriesno_id, ci.brand_id;
Here, if we are certain that each brand contains only one desc_brand, we can use an aggregate on it.
This is bcause applying aggregate only one value returns that value. I used MAX here.
Personally I would go with option (a) as it makes more sense.
Update on GROUP BY exception for desc_brand being NTEXT...
Cast desc_brand to NVARCHAR to avoid the exception.
CAST(desc_brand AS NVARCHAR(200))
Also I highly recommend using VARCHAR / NVARCHAR instead of any TEXT, CHAR etc. because they usually occupy more memory.
SELECT
id = SUBSTRING(group_concat,1,1),
color_code,
fruit,
group_concat
FROM(
SELECT distinct
m.color_code,
m.fruit,
group_concat = STUFF((SELECT ',' + CONVERT(varchar(10),md.id)
FROM [Test_1].[dbo].[Stuff] md
WHERE m.fruit = md.fruit
AND m.color_code = md.color_code
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM [Test_1].[dbo].[Stuff] m)x
use below code ..
SELECT distinct
m.color_code
, m.fruit
, group_concat = STUFF((
SELECT ',' + CONVERT(varchar(10),md.id)
FROM dbo.tablename md
WHERE m.fruit = md.fruit and m.color_code = md.color_code
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM dbo.tablename m
for second :
SELECT SN.id AS seriesid, B.id AS brandid, B.desc_brand ,count(*)
FROM [db1].[dbo].[series_no] SN
LEFT JOIN [db1].[dbo].[car_info] CI
ON CI.seriesno_id = SN.id
RIGHT JOIN [db1].[dbo].[brand] B
ON B.id = CI.brand_id
GROUP BY SN.id, B.id ,B.desc_brand
ORDER BY 4 ASC
I need to take data from a table that looks like this:
name | server | instance | version | user
----------|----------|------------|----------|--------- -
package_a | x | 1 | 1 | AB
package_b | x | 1 | 1 | TL
package_a | x | 2 | 4 | SK
package_a | y | 1 | 2 | MD
package_c | y | 1 | 4 | SK
package_b | y | 2 | 1 | SK
package_a | y | 2 | 1 | TL
package_b | x | 2 | 3 | TL
package_c | x | 2 | 1 | TL
and I need to put it in a table like that:
name | v_x_1 | u_x_1 | v_x_2 | u_x_2 | v_y_1 | u_y_1 | v_y_2 | u_y_2
----------|-------|-------|-------|-------|-------|-------|-------|-------
package_a | 1 | AB | 4 | SK | 2 | MD | 1 | TL
package_b | 1 | TL | 3 | TL | NULL | NULL | 1 | SK
package_c | NULL | NULL | 1 | TL | 4 | SK | NULL | NULL
I already tried INSERT with (SUB)SELECT, tried to INSERT package names first using DISTINCT and UPDATE afterwards, played around with PIVOT and stuff like that.
But I'm rather new to SQL and programming in general, so I couldn't come up with a solution. Since I not only have a version number in the source table but also nvarchar columns, It seems like PIVOT won't be the way to go, right?
You can use PIVOT on a sub query that uses UNION to separate the user and version values.
insert into YourNewTable (name, [v_x_1],[u_x_1],[v_x_2],[u_x_2],[v_y_1],[u_y_1],[v_y_2],[u_y_2])
select *
from (
select name, cast([version] as varchar(30)) as value, concat('v_',[server],'_',instance) as title from YourTable
union all
select name, [user] as value, concat('u_',[server],'_',instance) as title from YourTable
) q
pivot (max(value) FOR title IN (
[v_x_1],[u_x_1],[v_x_2],[u_x_2],[v_y_1],[u_y_1],[v_y_2],[u_y_2]
)
) pvt;
I made a INNER JOIN in stored procedure, but I don't know what to put to my WHERE clause to filter those column with null values and only shows those rows who has not null on a particular column.
CREATE PROCEDURE [dbo].[25]
#param1 int
AS
SELECT c.Name, c.Age, c2.Name, c2.Country
FROM Cus C
INNER JOIN Cus2 C2 ON c.id = c2.id
WHERE c2.country is not null and c2.id = #param1
Order by c2.Country
RETURN 0
ID 1
+-----+----+---------+---------+
| QID | ID | Name | Country |
+-----+----+---------+---------+
| 1 | 1 | Null | PH |
| 2 | 1 | Null | CN |
| 3 | 1 | Japhet | USA |
| 4 | 1 | Abegail | UK |
| 5 | 1 | Norlee | Ger |
+-----+----+---------+---------+
ID 2
+-----+----+----------+---------+
| QID | ID | Name | Country |
+-----+----+----------+---------+
| 1 | 2 | Null | PH |
| 2 | 2 | Null | CN |
| 3 | 2 | Reynaldo | USA |
| 4 | 2 | Abegail | UK |
| 5 | 2 | Norlee | Ger |
+-----+----+----------+---------+
ID 3
+-----+----+----------+---------+
| QID | ID | Name | Country |
+-----+----+----------+---------+
| 1 | 3 | Gab | PH |
| 2 | 3 | Null | CN |
| 3 | 3 | Reynaldo | USA |
| 4 | 3 | Abegail | UK |
| 5 | 3 | Norlee | Ger |
+-----+----+----------+---------+
I want when I choose any of the user in the C Table it will display the C child table data and remove the null name rows and remain the rows with not null name column.
Desired Result:
C Table (Parent)
+----+---------+-----+
| ID | Name | Age |
+----+---------+-----+
| 3 | Abegail | 31 |
+----+---------+-----+
C2 Table (Child)
+-----+----+----------+---------+
| QID | ID | Name | Country |
+-----+----+----------+---------+
| 1 | 3 | Gab | PH |
| 3 | 3 | Reynaldo | USA |
| 4 | 3 | Abegail | UK |
| 5 | 3 | Norlee | Ger |
+-----+----+----------+---------+
WHERE column IS NOT NULL is the syntax to filter out NULL values.
Solution 1: test not null value
Example:
WHERE yourcolumn IS NOT NULL
Solution 2: test comparaison value in your where clause (comparaison substract null values)
Examples:
WHERE yourcolumn = value
WHERE yourcolumn <> value
WHERE yourcolumn in ( value)
WHERE yourcolumn not in ( value)
WHERE yourcolumn between value1 and value2
WHERE yourcolumn not between value1 and value2
I have a table with some data, something like this:
+---------+---------+---------+---------+-------------+
| Column1 | Column2 | Column3 | Column4 | Column5 |
+---------+---------+---------+---------+-------------+
| 38073 | 16 | abc | 444 | 4/28/2015 |
| 38076 | 70 | gug | 555 | 4/30/2015 |
| 38098 | 13 | yyy | 111 | 5/12/2015 |
| 38098 | 13 | yyy | 112 | 5/13/2015 |
| 38098 | 13 | yyy | 113 | 5/14/2015 |
| 38098 | 13 | yyy | 114 | 5/15/2015 |
| 38100 | 17 | abc | 115 | 5/13/2015 |
+---------+---------+---------+---------+-------------+
What I want to do is to have the values from Columns 4 and 5 on a single row, something like this :
+---------+----------+-----------+----------+-----------+----------+-----------+----------+-------------+
| Col1 | Col4Val1 | Col5Val1 | Col4Val2 | Col5Val2 | Col4Val3 | Col5Val3 | Col4Val4 | Col5Val4 |
+---------+----------+-----------+----------+-----------+----------+-----------+----------+-------------+
| 38073 | 444 | 4/28/2015 | null | null | null | null | null | null |
| 38076 | 555 | 4/30/2015 | null | null | null | null | null | null |
| 38098 | 111 | 5/12/2015 | 112 | 5/13/2015 | 113 | 5/14/2015 | 114 | 5/15/2015 |
+---------+----------+-----------+----------+-----------+----------+-----------+----------+-------------+
Appreciate the help if possible.
Thank you.
Bogdan
You can use a UNION to unpivot the data with a CTE, then PIVOT the columns. You can achieve this dynamically too, there are hundreds of articles that will show you how to do that:
;WITH CTE AS (
SELECT [Column1], CAST([Column4] AS VARCHAR) AS [ColumnVals], 'Col4Val'+CAST(ROW_NUMBER() OVER(PARTITION BY [Column1] ORDER BY (SELECT 1)) AS VARCHAR) AS [Pivot]
FROM Table1
UNION
SELECT [Column1], [Column5], 'Col5Val'+CAST(ROW_NUMBER() OVER(PARTITION BY [Column1] ORDER BY (SELECT 1)) AS VARCHAR) AS [Pivot]
FROM Table1)
SELECT [Column1], [Col4Val1], [Col5Val1], [Col4Val2], [Col5Val2], [Col4Val3], [Col5Val3], [Col4Val4], [Col5Val4]
FROM CTE
PIVOT (MAX([ColumnVals]) FOR [Pivot] IN ([Col4Val1], [Col5Val1], [Col4Val2], [Col5Val2], [Col4Val3], [Col5Val3], [Col4Val4], [Col5Val4])) PIV
Here's a working fiddle: http://sqlfiddle.com/#!6/e992f/1