Update second column into one - sql-server

I would like to update multiple colums into one column of an another table:
I am using MS SQl Server
Table 1:
Num | ColumnA | ColumnB |
--------------------------
1 | Peter | Mueller |
1 | Jonny | Corleone |
2 | John | Cohn |
1 | Sarah | Wood |
Now I want to update ColumnA and ColumnB into ColumA of Table2 like this:
Table2:
Num | ColumnC |
----------------------------------------------------
1 | Peter, Mueller, Jonny, Corleone, Sarah, Wood |
2 | John, Cohn |

It is not good practice to store multiple values in a single column. If you need to do this though, then try this:
SELECT adress,
num,
Stuff((SELECT ',' + ColumnA + ',' + ColumnB
FROM table1 a
WHERE a.Num = b.Num
FOR xml path('')), 1, 1, '')
FROM table1 b
GROUP BY num

Related

SQL Server: how count from value from dynamic columns?

SQL Server: how count from value from dynamic columns?
I have data:
+ Subject
___________________
| SubID | SubName |
|-------|---------|
| 1 | English |
|-------|---------|
| 2 | Spanish |
|-------|---------|
| 3 | Korean |
|_______|_________|
+ Student
______________________________________
| StuID | StuName | Gender | SubID |
|---------|---------|--------|--------|
| 1 | David | M | 1,2 |
|---------|---------|--------|--------|
| 2 | Lucy | F | 2,3 |
|_________|_________|________|________|
I want to query result as:
____________________________________
| SubID | SubName | Female | Male |
|--------|---------|--------|------|
| 1 | English | 0 | 1 |
|--------|---------|--------|------|
| 2 | Spanish | 1 | 1 |
|--------|---------|--------|------|
| 3 | Koean | 1 | 0 |
|________|_________|________|______|
This is my query:
SELECT
SubID, SubName, 0 AS Female, 0 AS Male
FROM Subject
I don't know to replace 0 with real count.
Because you made the mistake of storing CSV data in your tables, we will have to do some SQL Olympics to get your result set. We can try joining the two tables on the condition that the SubID from the subject table appears somewhere in the CSV list of IDs in the student table. Then, aggregated by subject and count the number of males and females.
SELECT
s.SubID,
s.SubName,
COUNT(CASE WHEN st.Gender = 'F' THEN 1 END) Female,
COUNT(CASE WHEN st.Gender = 'M' THEN 1 END) Male
FROM Subject s
LEFT JOIN Student st
ON ',' + CONVERT(varchar(10), st.SubID) + ',' LIKE
'%,' + CONVERT(varchar(10), s.SubID) + ',%'
GROUP BY
s.SubID,
s.SubName;
Demo
But, you would be best off refactoring your table design to normalize the data better. Here is an example of a student table which looks a bit better:
+---------+---------+--------+--------+
| StuID | StuName | Gender | SubID |
+---------+---------+--------+--------+
| 1 | David | M | 1 |
+---------+---------+--------+--------+
| 1 | David | M | 2 |
+---------+---------+--------+--------+
| 2 | Lucy | F | 2 |
+---------+---------+--------+--------+
| 2 | Lucy | F | 3 |
+---------+---------+--------+--------+
We can go a bit further, and even store the metadata separately from the StuID and SubID relationship. But even using just the above would have avoided the ugly join condition.
If the version of your SQL Server is SQL Server or above, you could use STRING_split function to get expected results.
create table Subjects
(
SubID int,
SubName varchar(30)
)
insert into Subjects values
(1,'English'),
(2,'Spanish'),
(3,'Korean')
create table student
(
StuID int,
StuName varchar(30),
Gender varchar(10),
SubID varchar(10)
)
insert into student values
(1,'David','M','1,2'),
(2,'Lucy','F','2,3')
--Query
;WITH CTE AS
(
SELECT
S.Gender,
S1.value AS SubID
FROM student S
CROSS APPLY STRING_SPLIT(S.SubID,',') S1
)
select
T.SubID,
T.SubName,
COUNT(CASE T1.Gender WHEN 'F' THEN 1 END) AS Female,
COUNT(CASE T1.Gender WHEN 'M' THEN 1 END) AS Male
from Subjects T
LEFT JOIN CTE T1 ON T.SubID=T1.SubID
GROUP BY T.SubID,T.SubName
ORDER BY T.SubID
--Output
/*
SubID SubName Female Male
----------- ------------------------------ ----------- -----------
1 English 0 1
2 Spanish 1 1
3 Korean 1 0
*/

Insert two column values into single SQL Server

The following two tables Table 1 and Table 2 are given-
Table 1
+-----+------+---------+
| ID | Name | Earning |
+-----+------+---------+
| 101 | John | HRA |
| 101 | John | Travel |
| 102 | Andy | Travel |
+-----+------+---------+
Table 2
+-----+------+---------+
| ID | Name |Deduction|
+-----+------+---------+
| 101 | John | ENP |
| 102 | Andy | ENP |
| 102 | Andy | RA |
+-----+------+---------+
and I need to create a third table Table 3 with following columns
I have already created two columns ID and Name .I only need EarningOrDeduction column.
With
INSERT INTO Table3 (ID, Name, EarningOrDeduction)
SELECT ID, Name, Earning FROM Table1
UNION ALL
SELECT ID, Name, Deduction FROM Table2;
I'm getting
Table 3
+-----+------+------------------+
| ID | Name |EarningOrDeduction|
+-----+------+------------------+
| 101 | John | HRA |
| 101 | John | Travel |
| 102 | Andy | Travel |
| 101 | John | ENP |
| 102 | Andy | ENP |
| 102 | Andy | RA |
+-----+------+------------------+
But I want output as
Table 3
+-----+------+------------------+
| ID | Name |EarningOrDeduction|
+-----+------+------------------+
| 101 | John | HRA |
| 101 | John | Travel |
| 101 | John | ENP |
| 102 | Andy | Travel |
| 102 | Andy | ENP |
| 102 | Andy | RA |
+-----+------+------------------+
You can select both table data with Union clause.
And if you don't want to insert already entered values use following query.
INSERT INTO Table3 (EarningOrDeduction)
SELECT X FROM(
SELECT Earning X FROM Table1
UNION
SELECT Deduction X FROM Table2
) T
LEFT JOIN Table3 T3 ON T.X=T3.EarningOrDeduction
WHERE T3.EarningOrDeduction IS NULL
You could try inserting a union of values from the two tables:
INSERT INTO Table3 (ID, Name, EarningOrDeduction)
SELECT ID, Name, Earning FROM Table1
UNION ALL
SELECT ID, Name, Deduction FROM Table2;
Or, if you don't really want to populate Table3 with these values, you could just run the above select without the first insert line.
UNION ALL with Order by should work.
INSERT INTO Table3 (ID, Name, EarningOrDeduction)
SELECT ID, Name, EarningOrDeduction from
(SELECT ID, Name, Earning as [EarningOrDeduction] FROM Table1
UNION ALL
SELECT ID, Name, Deduction FROM Table2) ORDER BY ID, Name;
I assume Earnings and Deduction will not produce duplicate value for particular Name.

Return column names based on which holds the maximum value in the record

I have a table with the following structure ...
+--------+------+------+------+------+------+
| ID | colA | colB | colC | colD | colE | [...] etc.
+--------+------+------+------+------+------+
| 100100 | 15 | 100 | 90 | 80 | 10 |
+--------+------+------+------+------+------+
| 100200 | 10 | 80 | 90 | 100 | 10 |
+--------+------+------+------+------+------+
| 100300 | 100 | 90 | 10 | 10 | 80 |
+--------+------+------+------+------+------+
I need to return a concatenated value of column names which hold the maximum 3 values per row ...
+--------+----------------------------------+
| ID | maxCols |
+--------+----------------------------------+
| 100100 | colB,colC,colD |
+--------+------+------+------+------+------+
| 100200 | colD,colC,colB |
+--------+------+------+------+------+------+
| 100300 | colA,colB,colE |
+--------+------+------+------+------+------+
It's okay to not concatenate the column names, and have maxCol1 | maxCol2 | maxCol3 if that's simpler
The order of the columns is important when concatenating them
The number of columns is limited and not dynamic
The number of rows is many
You could use UNPIVOT and get TOP 3 for each ID
;with temp AS
(
SELECT ID, ColValue, ColName
FROM #SampleData sd
UNPIVOT
(
ColValue For ColName in ([colA], [colB], [colC], [colD], [colE])
) unp
)
SELECT sd.ID, ca.ColMax
FROM #SampleData sd
CROSS APPLY
(
SELECT STUFF(
(
SELECT TOP 3 WITH TIES
',' + t.ColName
FROM temp t
WHERE t.ID = sd.ID
ORDER BY t.ColValue DESC
FOR XML PATH('')
)
,1,1,'') AS ColMax
) ca
See demo here: http://rextester.com/CZCPU51785
Here is one trick to do it using Cross Apply and Table Valued Constructor
SELECT Id,
maxCols= Stuff(cs.maxCols, 1, 1, '')
FROM Yourtable
CROSS apply(SELECT(SELECT TOP 3 ',' + NAME
FROM (VALUES (colA,'colA'),(colB,'colB'),(colC,'colC'),
(colD,'colD'),(colE,'colE')) tc (val, NAME)
ORDER BY val DESC
FOR xml path, type).value('.[1]', 'nvarchar(max)')) cs (maxCols)
If needed it can be made dynamic using Information_schema.Columns

Distinct 2 field AS one column SQL SERVER

can i SELECT distinct 2 fields (provfrom, provto) on table AS one column
with condition :
- values of 2 fields is never same in one row
- values in field provfrom can be inside field provto but in different row
- values in field provto can be inside field provfrom but in different row
example :
i have 2 column as below
-------------------------
| provfrom | provto |
-------------------------
| 2 | 4 |
| 3 | 7 |
| 3 | 7 |
| 5 | 2 |
| 5 | 2 |
| 7 | 2 |
| 7 | 2 |
| 1 | 5 |
| 2 | 5 |
| 2 | 8 |
| 5 | 8 |
-------------------------
the result that i want by disticnt is as below
-------------
| prov |
-------------
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 7 |
| 8 |
-------------
Can i do this in sql server?
i try to found out by explore google, but not found it
Thanks
You can use Union keyword which will give distinct elements from Both Tables
select provfrom from mytable
union
select provTo from mytable
You can either do this with a union or by using apply, the apply has less IO so I would go with the apply query.
create table #temp
(
provfrom tinyint,
provto tinyint
);
insert into #temp (provfrom, provto)
values (2,4),(3,7),(3,7),
(5,2),(5,2),(7,2),
(7,2),(1,5),(2,5),
(2,8),(5,8);
set statistics io on;
select distinct
a.provfromto
from #temp as t
cross apply (values (t.provfrom),(t.provto)) as a(provfromto);
select provfrom from #temp
union
select provTo from #temp
set statistics io off;
drop table #temp;
Try this:
select t.prov
from
(select provfrom as prov
from yourtable
union
select provto
from yourtable) as t
order by t.prov
UNION function apply a distinct clause, so you'll get all value per one occurence.
The external query about ordering your result set

T-SQL Transposing column headers into Rows

I want to transpose my table.
I have simple 'Person' table as shown below.
+---+----------+------------+------------+----------------------+
|ID | Person | BirthDate | Phone | Email |
+---+----------+------------+------------+----------------------+
| 1 | Tom | 1985-11-08 | 1111111111 | tom#somedomain.com |
+---+----------+------------+------------+----------------------+
| 2 | Dick | 1982-02-24 | 2222222222 | dick#otherdomain.com |
+---+----------+------------+------------+----------------------+
| 3 | Harry | 1986-04-17 | 3333333333 | harry#thatdomain.com |
+---+----------+------------+------------+----------------------+
And I want this table to be transposed like below.
+-----------+--------------------+----------------------+----------------------+
| Key | Value1 | Value2 | Value3 |
+-----------+--------------------+----------------------+----------------------+
| ID | 1 | 2 | 3 |
+-----------+--------------------+----------------------+----------------------+
| Person | Tom | Dick | Harry |
+-----------+--------------------+----------------------+----------------------+
| BirthDate | 1985-11-08 | 1982-02-24 | 1986-04-17 |
+-----------+--------------------+----------------------+----------------------+
| Phone | 1111111111 | 2222222222 | 3333333333 |
+-----------+--------------------+----------------------+----------------------+
| Email | tom#somedomain.com | dick#otherdomain.com | harry#thatdomain.com |
+-----------+--------------------+----------------------+----------------------+
I am using MS SQL server 2008 R2.
Try this.. First u need to unpivot the columns using Cross apply to get the data in single row. Then pivot that row to get the result.
CREATE TABLE #tt
(ID INT,Person VARCHAR(50),BirthDate DATE,
Phone BIGINT,Email VARCHAR(50)
)
INSERT INTO #tt
VALUES (1,'Tom','1985-11-08',1111111111,'tom#somedomain.com' ),
( 2,'Dick','1982-02-24',2222222222,'dick#otherdomain.com'),
( 3,'Harry ','1986-04-17',3333333333,'harry#thatdomain.com' )
SELECT [key],
Max([value1]) [value1],
Max([value2]) [value2],
Max([value3]) [value3]
FROM (SELECT 'value' + CONVERT(VARCHAR(30), id) valued,
*
FROM #tt
CROSS apply (VALUES ('ID',
CONVERT(VARCHAR(50), ID)),
('Person',Person),
('BirthDate',CONVERT(VARCHAR(50), BirthDate)),
('Phone',CONVERT(VARCHAR(50), Phone)),
('Email',Email)) cp ([key], data))a
PIVOT (Max(data)
FOR valued IN([value1],[value2],[value3])) piv
GROUP BY [key]
DYNAMIC VERSION
Declare #cols varchar(max)='',#aggcols varchar(max)='',#sql nvarchar(max)
SELECT #cols+= ',value' + CONVERT(VARCHAR(30), id)
FROM #tt
SELECT #aggcols+= ',max([value' + CONVERT(VARCHAR(30), id) +']) value' + CONVERT(VARCHAR(30), id)
FROM #tt
select #cols= right(#cols,LEN(#cols)-1)
select #aggcols =right(#aggcols,LEN(#aggcols)-1)
set #sql = 'SELECT [key],
'+#aggcols+'
FROM (SELECT ''value'' + CONVERT(VARCHAR(30), id) valued,
*
FROM #tt
CROSS apply (VALUES (''ID'',CONVERT(VARCHAR(50), ID)),
(''Person'',Person),
(''BirthDate'',CONVERT(VARCHAR(50), BirthDate)),
(''Phone'',CONVERT(VARCHAR(50), Phone)),
(''Email'',Email)) cp ([key], data))a
PIVOT (Max(data)
FOR valued IN('+#cols+')) piv
GROUP BY [key] '
execute sp_executesql #sql
OUTPUT
+----------+--------------------+---------------------+----------------------+
|key | value1 | value2 | value3 |
+----------+--------------------+---------------------+----------------------+
|BirthDate | 1985-11-08 | 1982-02-24 | 1986-04-17 |
+----------+--------------------+---------------------+----------------------+
|Email | tom#somedomain.com |dick#otherdomain.com | harry#thatdomain.com |
+----------+--------------------+---------------------+----------------------+
|ID | 1 | 2 | 3 |
+----------+--------------------+---------------------+----------------------+
|Person | Tom | Dick | Harry |
+----------+--------------------+---------------------+----------------------+
|Phone | 1111111111 | 2222222222 | 3333333333 |
+----------+--------------------+---------------------+----------------------+

Resources