Concatenate Two Separate Columns of Text Per ID - sql-server

I know it's possible to concatenate one row using the following code. For example making one row of StudentName text per subject ID.
Example Data
Patient_FIN StudentName
---------- -------------
1 Mary
1 John
2 Alaina
2 Edward
Expected Results
Patient_FIN StudentName
---------- -------------
1 Mary, John
2 Alaina, Edward
Code used to get the above output
SELECT DISTINCT ST2.pt_fin,
SUBSTRING(
(
SELECT ST1.StudentName + ',' AS [text()]
FROM ED_ORDERS_IMPORT_MASTER ST1
WHERE ST1.Patient_FIN = ST2.Patient_FIN
ORDER BY ST1.Patient_FIN
FOR XML PATH ('')
) , 2, 1000) [Patient]
FROM ED_ORDERS_IMPORT_MASTER ST2
However, let's say I have the same data and an additional row that I'd also like to concatenate into a separate column based on Patient_FIN:
Patient_FIN StudentName Color
---------- ------------- ----------
1 Mary Blue
1 John Red
2 Alaina Red
2 Edward White
Desired Results
Patient_FIN StudentName Color
---------- ------------- ----------
1 Mary, John Blue, Red
2 Alaina, Edward Red, White
How can I edit the above code to produce the desired results? Thanks!

You can use STUFF to get your desired output as below-
SELECT Patient_FIN,
STUFF
(
(
SELECT ',' + StudentName
FROM your_table
WHERE Patient_FIN = A.Patient_FIN
FOR XML PATH ('')), 1, 1, ''
) StudentName,
STUFF
(
(
SELECT ',' + Color
FROM your_table
WHERE Patient_FIN = A.Patient_FIN
FOR XML PATH ('')), 1, 1, ''
) Color
FROM your_table A
GROUP BY Patient_FIN
Output is-
Patient_FIN StudentName Color
1 Mary,John Blue,Red
2 Alaina,Edward Red,White

Related

Merge multiple rows for each ID so that the most information is collected

I have multiple rows for each ID, I don't want the most recent, I would like to merge these into the most complete entry for each ID. Each ID may have a different number of associated rows.
I'd like to do this in either Alteryx or SQL but not sure at all where to begin.
for example:
row ID ColA ColB ColC
1 1234 red
2 1234 purple red
3 1234 blue
Desired result:
row ID ColA ColB ColC
1 1234 Purple red blue
It looks like a simple aggregation should do the trick.
For Example
Select row = min(row)
,ID
,ColA = max(ColA)
,ColB = max(ColB)
,ColC = max(ColC)
From YourTable
Group By ID

How to sum duplicate values while all of my other columns are included in my report?

MY QUESTION DESCRIPTION:
This is my table in SQL SERVER: (The contained data is previously entered.)
ID CodeColumn NameColumn QNT UnitCost Description
1 121 lute 3 100000 blah
3 122 Leather 10 50000 blah
I have a program in C# which has reletive textboxes for all of these colums.
this is my addgoods store procedure:
CREATE procedure AddGoods
#QCode nvarchar(50),
#QName nvarchar(50),
#QQNT int,
#QUnitCost int,
#QDR int,
#QDesc nvarchar(250)
as
Insert Into GoodsTable1(CodeColumn,NameColumn,QTYColumn,UnitCostColumn,DiscountRateColumn,DescriptionColumn) values (#QCode,#QName,#QQNT,#QUnitCost,#QDR,#QDesc)
return
this is my desired report store procedure:(which yells at me)
SELECT ROW_NUMBER() Over (ORDER BY namecolumn) AS RowNumberColumn,
CodeColumn,
NameColumn,
SUM(QNTColumn) AS QNT,
UnitCostColumn,
DiscountRateColumn,
SUM(UnitCostColumn) AS Total,
DescriptionColumn
FROM GoodsTable1
GROUP BY NameColumn
When I get input from user, every time that the same code or name is entered, I want the value of the quantity column to be the sum of the reletive values of the comodities within the input.
Also I want the values of UnitCost Column to be summed and showed as TotalColumn.
for example:
(as I said these datas are previously entered)
1 121 lute 3 100000 blah
3 122 Leather 10 50000 blah
when I get new input from user eg. :
121 lute 5 100000 information
I want to be able to report this from my data base:
121 lute 8 100000 *(blah gets deleted)* information
I tried this in my query to avoid the error:
select
NameColumn,
SUM(QTYColumn),
SUM(UnitCostColumn)
FROM GoodsTable1
GROUP BY NameColumn
it works but I need other columns in as well.
Your question is pretty unclear and contains some discrepancies. Let's try to clean it up:
Your column is called QNT in sample data and QTY in query. I suspect this is just a mistake. Same for other columns. Let's assume your table looks like in the sample below:
DECLARE #table TABLE
(
ID int identity,
CodeColumn varchar(8),
NameColumn nvarchar(20),
QtyColumn int,
UnitCostColumn decimal(15,2),
DescriptionColumn nvarchar(MAX),
DiscountRateColumn decimal(15,2) DEFAULT (0.0)
)
INSERT #table(CodeColumn, NameColumn, QtyColumn, UnitCostColumn, DescriptionColumn) VALUES
(121, 'lute', 3, 100000, 'blah'),
(122, 'Leather', 10, 50000, 'blah'),
(121, 'lute', 5, 100000, 'blah')
Then, you can calculate running total as follows:
SELECT *,
SUM(QtyColumn) OVER (PARTITION BY CodeColumn ORDER BY ID) RunningQty,
SUM(UnitCostColumn) OVER (PARTITION BY CodeColumn ORDER BY ID) RunningCost
FROM #table
Result:
ID CodeColumn NameColumn QtyColumn UnitCostColumn DescriptionColumn DiscountRateColumn RunningQty RunningCost
---- ---------- ------------ ----------- --------------- ------------------ -------------------- ----------- ------------
1 121 lute 3 100000.00 blah 0.00 3 100000.00
3 121 lute 5 100000.00 blah 0.00 8 200000.00
2 122 Leather 10 50000.00 blah 0.00 10 50000.00
I also expect, that you are trying to preserve only last record from each group. Above query could be rewritten as:
SELECT */*select all columns you need*/ FROM (
SELECT *,
SUM(QtyColumn) OVER (PARTITION BY CodeColumn ORDER BY ID) RunningQty,
SUM(UnitCostColumn) OVER (PARTITION BY CodeColumn ORDER BY ID) RunningCost,
ROW_NUMBER() OVER (PARTITION BY CodeColumn ORDER BY ID DESC) LastRank
FROM #table
) T WHERE T.LastRank=1
Result:
ID CodeColumn NameColumn QtyColumn UnitCostColumn DescriptionColumn DiscountRateColumn RunningQty RunningCost LastRank
--- ---------- ----------- ----------- --------------- ------------------ ------------------- ----------- ------------ ---------
3 121 lute 5 100000.00 blah 0.00 8 200000.00 1
2 122 Leather 10 50000.00 blah 0.00 10 50000.00 1
To provide more compact and efficient solution, more information is needed.
Try to change the query like this
SELECT ROW_NUMBER() Over (ORDER BY namecolumn) AS RowNumberColumn,
CodeColumn,
NameColumn,
SUM(QTYColumn) AS QTY,
UnitCostColumn,
DiscountRateColumn,
SUM(UnitCostColumn) AS Total,
DescriptionColumn
FROM GoodsTable1
GROUP BY CodeColumn, NameColumn, UnitCostColumn, DiscountRateColumn, DescriptionColumn
You can add extra fields that you want in both SELECT and GROUP BY clauses but be sure to have unique values because you will get wrong results.

Can I output data from other table's column that meet the specific criteria to show in the single row

I'm currently learning SQL using SSMS 2014 and I was wondering is it possible to make a SELECT statement so that it will join the two tables but in such a way that data from the second table is shown in the separate columns for the different values certain criteria is met, so that the output is shown in the same row?
I am not sure how to paraphrase this question properly so I will give an example.
For example if we have 2 tables:
ProductID ProductName More columns...
--------- ----------- ...
1 Shoes ...
2 Shirt ...
Product Color Amount
------- ----- ------
1 Black 5
1 Red 3
2 Black 1
2 Red 6
2 White 3
I want the select statement to return something like this:
ProductName Col:Black Col:Red Col:White
----------- --------- ------- ---------
Shoes 5 3 NULL
Shirt 1 6 3
How can I make such query and is it even possible in T-SQL? Is there a way to specify IF statement in SQL such that it return date if some condition is met.
So far I am only able to get output like this but it's not what i want:
Product Color Amount
------- ----- ------
Shoes Black 5
Shoes Red 3
Shirt Black 1
Shirt Red 6
Shirt White 3
You can do it with conditional aggregation using CASE EXPRESSION :
SELECT t.productName,
MAX(CASE WHEN s.Color = 'Black' then s.amount end) as Col_Black,
MAX(CASE WHEN s.Color = 'Red' then s.amount end) as Col_Red,
MAX(CASE WHEN s.Color = 'White' then s.amount end) as Col_White
..........
FROM Table1 t
LEFT OUTER JOIN Table2 s
ON(t.productID = s.product)
GROUP BY t.productName

Converting rows to columns

I have a query with the columns 'Name', 'Amount', and 'ReasonId'. I want to sum the amount and put the reasons on one row to keep every name to a single line. There are about 50 distinct ReasonId's so I do not want to name the column the name of the ReasonId's. Instead, I would like to name the columns 'Reason1', 'Reason2', 'Reason3', and 'Reason4'. One single name can have up to 4 different reasons.
I have this:
Name Amount ReasonId
-------------------------
Bob $5 7
Bob $8 6
John $2 8
John $5 9
John $3 9
John $8 4
I want to produce the following:
Name Amount Reason1 Reason2 Reason3 Reason4
-----------------------------------------------------
Bob $13 7 6 NULL NULL
John $18 8 9 4 NULL
One way to do this is to use the dense_rank window function to number the rows, and then use conditional aggregation to put the reason in the correct columns.
I can't see anything that would give the specific order of the reason columns though, maybe there is some column missing that provides the order?
with cte as (
select
name,
reasonid,
amount,
dense_rank() over (partition by name order by reasonid) rn
from your_table
)
select
name,
sum(amount) amount,
max(case when rn = 1 then reasonid end) reason1,
max(case when rn = 2 then reasonid end) reason2,
max(case when rn = 3 then reasonid end) reason3,
max(case when rn = 4 then reasonid end) reason4
from cte
group by name
If you have some column that gives the order you want then change the order by clause used in the dense_rank function.
Sample SQL Fiddle (using PG as MSSQL seems to be offline).
The output from the query above would be:
| name | amount | reason1 | reason2 | reason3 | reason4 |
|------|--------|---------|---------|---------|---------|
| Bob | 13 | 6 | 7 | (null) | (null) |
| John | 18 | 4 | 8 | 9 | (null) |
You could also use a pivot to achieve this; if you know the columns you can enter them in the script, but if not, you can use dynamic sql (there are reasons why you might want to avoid the dynamic solution).
The advantage of this route is that you can enter the column list in a table and then changes to that table will result in changes to your output with change to the script involved. The disadvantages are all those associated with dynamic SQL.
In the interests of variation, here is a dynamic SQL solution using temp tables to hold your data, since a different possibility has been provided:
-- set up your data
CREATE TABLE #MyTab (Name VARCHAR(4), Amount INT, ReasonId INT)
CREATE TABLE #AllPossibleReasons (Id INT,Label VARCHAR(10))
INSERT #AllPossibleReasons
VALUES
(1,'Reason1')
,(2,'Reason2')
,(3,'Reason3')
,(4,'Reason4')
,(5,'Reason5')
,(6,'Reason6')
,(7,'Reason7')
,(8,'Reason8')
,(9,'Reason9')
INSERT #MyTab
VALUES
('Bob',7,7)
,('Bob',8,6)
,('John',2,8)
,('John',5,9)
,('John',3,9)
,('John',8,4)
-----------------------------------------------------------------------------
-- The actual query
DECLARE #ReasonList VARCHAR(MAX) = ''
DECLARE #SQL VARCHAR(MAX)
SELECT #ReasonList = #ReasonList + ',' + QUOTENAME(Label)
FROM #AllPossibleReasons
SET #ReasonList = SUBSTRING(#ReasonList,2,LEN(#ReasonList))
SET #SQL =
'SELECT Name,Value,' + #ReasonList + ' FROM
(SELECT
M.Name,SUM(Amount) AS This, Label, SUM(Total.Value) AS Value
FROM
#MyTab AS M
INNER JOIN #AllPossibleReasons AS Reason ON M.ReasonId = Reason.Id
INNER JOIN(SELECT T.Name, SUM(Amount)Value
FROM #MyTab T GROUP BY T.Name) AS Total ON M.Name = Total.Name
GROUP BY M.Name, Reason.Label) AS Up
PIVOT (SUM(THis) FOR Label IN (' + #ReasonList + ')) AS Pvt'
EXEC (#SQL)
DROP TABLE #AllPossibleReasons
DROP TABLE #MyTab
Working from the information in ListAGG in SQLSERVER, I came up with this somewhat ugly example:
with tbl1 as (
-- Set up initial data set
select 'Bob' name, 5 amount, 7 ReasonId
union all select 'Bob' , 3, 4
union all select 'Bob', 2, 1
union all select 'Brian', 8, 2
union all select 'Bob', 6, 4
union all select 'Brian', 1, 3
union all select 'Tim', 2, 2)
, TBL2 AS ( -- Add a blank to separate the concatenation
SELECT NAME
, AMOUNT
, CAST(ReasonId as varchar) + ' ' ReasonId from tbl1
)
select ta.name
, Total
, ReasonIds from (
(select distinct name, stuff((select distinct '' + t2.ReasonId from tbl2 t2
where t1.name = t2.name
for xml path(''), type).value('.','NVARCHAR(MAX)'),1,0,' ') ReasonIds from tbl2 t1) ta
inner join ( select name, sum(amount) Total from tbl1 group by name) tb on ta.name = tb.name) ;
This converts TBL1 to the following:
name Total ReasonIds
Bob 16 1 4 7
Brian 9 2 3
Tim 2 2

Many to many relationship without duplicates

I'm trying to solve a problem with many-to-many relation.
I have 3 tables:
Article
-------
ArticleID
FilePath
Title
Description
Author
--------
AuthorID
AuthorFName /* Father last name */
AuthorMName /* Mother last name */
AuthorName
ArticleAuthor
-------------
AuthorID
ArticleID
Foreign KEY (AuthorID) REFERENCES Author(AuthorID),
FOREIGN KEY (ArticleID) REFERENCES Article(ArticleID),
PRIMARY KEY (AuthorID, ArticleID)
The Author table has 2 authors:
ID FName MName Name
-- ----- ----- ----
1 XXX YYY AAA
2 MMM NNN BBB
The Article:
ID FilePath Title Description (optional)
-- -------- ----- ----------------------
1 /path/file '....' ''
And the ArticleAuthor
ArticleID AuthorID
--------- --------
1 1
1 2
The query I'm using now is:
SELECT DISTINCT ArticleAuthor.ArticleID,
STUFF(
(
SELECT ',' + CONCAT(AuthorFName, ' ', AuthorMName, ' ', AuthorName)
FROM FinderSchema.Author
WHERE Author.AuthorID = ArticleAuthor.AuthorID
FOR XML PATH(''), TYPE
).value('.', 'varchar(max)'), 1, 1, ''
) AS Authors
FROM FinderSchema.ArticleAuthor
LEFT JOIN FinderSchema.Author ON ArticleAuthor.AuthorID = Author.AuthorID
GROUP BY ArticleAuthor.ArticleID, ArticleAuthor.AuthorID
But it returns duplicates:
ArticleID Authors
--------- -------
1 XXX YYY AAA
1 MMM NNN BBB
So, is there a way to remove the duplicates? I have been reading about simulating GROUP_CONCAT (I'm targeting sql server 2008 RC 2), and that's the answer I've found (in stackoverflow, actually), but does not work for me (or many to many relationship, maybe).
Well, if someone can help me, I'll be grateful.
Thanks in advance :)
Try
SELECT ArticleID,
STUFF((SELECT DISTINCT ',' + a.AuthorFName + ' ' + a.AuthorMName + ' ' + a.AuthorName
FROM ArticleAuthor aa JOIN Author a
ON aa.AuthorID = a.AuthorID
WHERE aa.ArticleID = aa2.ArticleID
FOR XML PATH('')) , 1 , 1 , '' ) Authors
FROM ArticleAuthor aa2
GROUP BY ArticleID
Output:
| ARTICLEID | AUTHORS |
---------------------------------------
| 1 | MMM NNN BBB,XXX YYY AAA |
Here is SQLFiddle demo

Resources