Combining multiple records into one - sql-server

I'm using the following query:
DECLARE #Code varchar(6)
SELECT a.Code, a.Description, a.Time, b.id
FROM TableA a
LEFT OUTER JOIN TableB B ON a.id = b.id
WHERE a.Code = #Code
The issue I'm having is it's returning multiple records because of the outer join, something like the following:
Code Description Time B.id
5038 sample desc 4 108
5038 sample desc 4 632
5038 sample desc 4 633
5038 sample desc 4 197
5038 sample desc 4 503
What would be the best way to combine these into one record, with say a delimited list of b.id's?

For a delimited list you can go for XML Path clause. Here is the explanation: http://blog.sqlauthority.com/2013/04/05/sql-server-group-by-rows-and-columns-using-xml-path-efficient-concating-trick/

You can use this method found here
USE AdventureWorks
GO
DECLARE #listStr VARCHAR(MAX)
SELECT #listStr = COALESCE(#listStr+',' ,'') + Name
FROM Production.Product
SELECT #listStr
GO

Related

SQL Server - Generate the same random number into multiple columns

I would like to generate a random number (5 or 6 digits) but not just into one column, in 4 actually.
ID1 ID2 Type LotNumber ID3 ID4
-------------------------------------------------------------
721 721 1 Lot1 721 721
721 721 1 Lot1 721 721
Looking over the internet and several articles, mostly generate a random number to be used in just one column and that's it. Is there any way I can achieve this using SQL? Thanks
EDIT
I used this statement:
SELECT FLOOR(RAND(CHECKSUM(NEWID()))*(99999-10000+1)+1000) AS ID
VIEW
CREATE VIEW MyView
AS
SELECT
'' AS ID1,
'' AS ID2,
CASE WHEN t.fADTableField4Code='Aggregated' THEN CAST(1 AS BIT) ELSE CAST(2 AS BIT) END AS Type,
ISNULL(t.fADTableField3Code, '') AS LotNumber,
'' AS ID3,
'' AS ID4
FROM MyTable AS t
GO
XML File
The XML file has to look like this:
<Documents>
<Document>
<ID1>721</ID1>
<ID2>721</ID2>
<Type>1</Type>
<LotNumber>Lot1</LotNumber>
<ID3>721</ID3>
<Date>2018-12-04</Date>
<Details>
<Detail>
<ID4>721</ID4>
............
</Details>
</Document>
Try this and expand upon it.
select a.*
, b.id as ID1, b.id as ID2 --Get ID from applied table and use as often as you need
from (select 1) as a(ID) -- this is your main table
cross apply (SELECT FLOOR(RAND(CHECKSUM(NEWID()))*(99999-10000+1)+1000) AS ID) B
you can use either a CTE or a subquery
SELECT
ID1 = R.ID
,ID2 = R.ID
,ID3 = R.ID
,ID4 = R.ID
FROM
(SELECT
FLOOR(RAND(CHECKSUM(NEWID()))*(99999-10000+1)+1000)
AS ID
) R
;WITH R
AS
(SELECT
FLOOR(RAND(CHECKSUM(NEWID()))*(99999-10000+1)+1000)
AS ID
)
SELECT
ID1 = R.ID
,ID2 = R.ID
,ID3 = R.ID
,ID4 = R.ID
FROM R

Subtracting two columns from two different table with different data

i had this problem where i got two tables with different values.
TABLE 1:
Description Qty Amount
Proc 1 1 100
Proc 2 1 50
Proc 3 1 60
TABLE 2:
Description Payment
Proc 1 60
Proc 1 30
Proc 2 20
Proc 3 60
Proc 2 20
So, the result should be like:
Description Balance
Proc 1 10
Proc 2 40
Proc 3 0
How will i do this with select query? thanks.
I Already tried this code but it should first get the distinct rows of TABLE1 and sum distinct values from TABLE2 before subtracting the two tables.
SELECT FEESList.[Fee Description], sum(StudentBILLING.Quantity* StudentBILLING.Total- isnull(StudReceipts.Amount,0)) as Balance
FROM StudentBILLING INNER JOIN FEESList ON StudentBILLING.FeeID = FEESList.FeeID INNER JOIN SREGStudentInformation ON StudentBILLING.StudentID = SREGStudentInformation.ID INNER JOIN
SemesterList ON StudentBILLING.SemesterID = SemesterList.SemID INNER JOIN SchoolYear ON StudentBILLING.SYid = SchoolYear.[SY ID] FULL JOIN StudReceipts ON FEESList.FeeID = StudReceipts.FeeID
WHERE (SREGStudentInformation.[Student ID] = #StudentNumber) AND (SemesterList.[Sem.] = #Sem) AND (SchoolYear.[School Year] = #SchoolYear) AND
(FEESList.[Type of Fee] = 2) AND (StudentBILLING.Quantity* StudentBILLING.Total- ISNULL(StudReceipts.Amount,0))>0
GROUP BY FEESList.[Fee Description]
The result is from this;
Description Amount Payments
Proc 1 100 60
Proc 1 100 30
Proc 2 50 20
Proc 2 50 20
Proc 3 60 60
To this result which is not supposed to be.
Description Amount
Proc 1 100
Proc 2 50
Can you help me with this?
You could try below with sample data which you have provided
First, it will generated row numbers by using row_number() function partition by Description and Payment column which has duplicate payment made & sum() if have duplicate payments made
;WITH cte AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY [Description],
Payment ORDER BY [Description]) rn
FROM <TABLE 2>)
SELECT t.[Description],
CASE
WHEN COUNT(DISTINCT c.rn) > 1 THEN SUM(c.Payment)
ELSE MAX(t.amount) - SUM(c.Payment)
END [Balance]
FROM cte c
JOIN <TABLE 1> t ON t.[Description] = c.[Description]
GROUP BY t.[Description];
Result :
Description Balance
Proc 1 10
Proc 2 40
Proc 3 0
Note, But you could also check with your sample data what if one Description has both Payments made one is duplicate and other is unique ??
You're maths isn't correct
Proc 2 = Tabl1 50, Tab2 20+ 20 = balance = 10
DECLARE #tab1 TABLE ([Description] NVARCHAR(15), Qty INT, Amount INT )
INSERT INTO #tab1
([Description], Qty, Amount)
SELECT 'Proc 1',1,100 UNION ALL
SELECT 'Proc 2',1,50 UNION ALL
SELECT 'Proc 3',1,60
DECLARE #tab2 TABLE ([Description] NVARCHAR(15), Payment INT)
INSERT INTO #tab2
([Description], Payment)
SELECT 'Proc 1', 60 UNION ALL
SELECT 'Proc 1', 30 UNION ALL
SELECT 'Proc 2', 20 UNION ALL
SELECT 'Proc 3', 60 UNION ALL
SELECT 'Proc 2', 20
SELECT
T.[Description]
, Balance = (T.Qty * T.Amount) - X.Payment
FROM #tab1 T
INNER JOIN
(
SELECT
T.[Description]
, Payment = SUM(T.Payment)
FROM #tab2 T
GROUP BY T.[Description]
) X ON X.[Description] = T.[Description]
Left joining from a derived table will solve your issue. Alternatively you could have done it the slightly longer route by doing aggregation in a cte and then a join
select t1.description
,t1.amount - t2.sum as balance
from table1 t1
left join
(select description
,sum(payment) as sum
from table2
group by description) t2 on t1.description = t2.description
Just some food for thought:
You want to use a left join instead of an inner join. Left join says if it's in this first table and not second, show null. Inner join says show it only if it is in BOTH tables. And I'm guessing you want to show the balance even if a payment hasn't been made (just a guess but that's up to your report).
Here's the rextester sample you can play with.

How to merge list from SQL Server stored procedure

ALTER PROCEDURE [dbo].[spGetItemCategories]
AS
BEGIN
SET NOCOUNT ON;
--SELECT * FROM ItemCategories
SELECT
IC.Id, IC.Name ,C.Id AS CompanyId, C.Name AS CompanName
FROM
ItemCategories IC
JOIN
CompanyItems CI ON IC.Id = CI.ItemCategoryId
JOIN
Companies C ON CI.CompanyId = C.Id
--WHERE CI.CompanyId IN (SELECT TOP(100)* FROM Companies C)
END
This displays data like:
4 sdfs 14 Nestle
4 sdfs 15 Unilever
but I want to get like this:
4 sdfs 14 Nestle
15 Unilever
You can check this method but the same data.
declare #mytable table (compid int,compname varchar(20),itemid int, itemdesc varchar(20))
insert into #mytable
values
(1,'Company A',100,'Nestle'),
(1,'Company A',200,'UniLever'),
(2,'Company B',300,'Citrix'),
(2,'Company B',400,'SQL'),
(2,'Company B',500,'Oracle'),
(1,'Company B',600,'Microsoft')
select
iif(left(m1.ord_id,1)>1,NULL,m.compid) [CompID],
iif(left(m1.ord_id,1)>1,NULL,m.compname) [CompName],
m.itemid,
m.itemdesc
from #mytable m
inner join (
select distinct compid,row_number() over (partition by compid order by itemid) [ord_id], itemid
from #mytable) m1
on m.compid = m1.compid and m.itemid = m1.itemid
or CTE
;with cte as
(
select distinct compid,row_number() over (partition by compid order by itemid) [ord_id], itemid
from #mytable
)
select
iif(left(m1.ord_id,1)>1,NULL,m.compid) [CompID],
iif(left(m1.ord_id,1)>1,NULL,m.compname) [CompName],
m.itemid,
m.itemdesc
from #mytable m
inner join cte m1
on m.compid = m1.compid and m.itemid = m1.itemid
if you are not happy with nulls replace the fields
iif(left(m1.ord_id,1)>1,'',cast(m.compid as varchar)) [CompID],
iif(left(m1.ord_id,1)>1,'',m.compname) [CompName],
Result
CompID CompName itemid itemdesc
1 Company A 100 Nestle
200 UniLever
600 Microsoft
2 Company B 300 Citrix
400 SQL
500 Oracle

Is it possible to select records based on order of in clause?

I have a select statement in which I am using in clause.
Here is my table : MyTable
Id SKU
1 112
2 223
3 445
4 456
5 678
If I write:
SELECT Id
FROM MyTable
WHERE SKU IN (112,223,445, 456, 678)
I an not getting result as
1
2
3
4
5
Is there any way to get select result based on items order in the in clause.?
For your case ORDER BY id will be sufficient.
SELECT Id
FROM MyTable
WHERE SKU IN (112,223,445, 456, 678)
ORDER BY id
For general approach you could use JOIN with derived table like:
Demo
SELECT m.Id
FROM MyTable m
JOIN (VALUES (1, 112) ,(2,223) ,(3,445), (4,456), (5,678)) AS t(num, SKU)
ON m.SKU = t.SKU
ORDER BY t.num
If you use SQL Server 2008 you can use UNION ALL:
Demo2
;WITH cte AS
(
SELECT 112 AS SKU, 1 AS orderNum
UNION ALL
SELECT 223 AS SKU, 2 AS orderNum
UNION ALL
SELECT 445 AS SKU, 3 AS orderNum
UNION ALL
SELECT 456 AS SKU, 4 AS orderNum
UNION ALL
SELECT 678 AS SKU, 5 AS orderNum
)
SELECT m.Id
FROM #MyTable m
JOIN cte c
ON m.SKU = c.SKU
ORDER BY c.orderNum;
General approach that does not force you to create custom query you could use temp table with IDENTITY column like:
Demo3
CREATE TABLE #mySKU( orderNum INT IDENTITY(1,1), SKU INT);
INSERT INTO #mySKU
VALUES (112),(223),(445), (456), (678);
SELECT m.Id
FROM #MyTable m
JOIN #mySKU c
ON m.SKU = c.SKU
ORDER BY c.orderNum;
"Is there any way to get select result based on items order in the in clause?"
For this particular question the answer is no.

Combine multiple results in a subquery into a single comma-separated value

I've got two tables:
TableA
------
ID,
Name
TableB
------
ID,
SomeColumn,
TableA_ID (FK for TableA)
The relationship is one row of TableA - many of TableB.
Now, I want to see a result like this:
ID Name SomeColumn
1. ABC X, Y, Z (these are three different rows)
2. MNO R, S
This won't work (multiple results in a subquery):
SELECT ID,
Name,
(SELECT SomeColumn FROM TableB WHERE F_ID=TableA.ID)
FROM TableA
This is a trivial problem if I do the processing on the client side. But this will mean I will have to run X queries on every page, where X is the number of results of TableA.
Note that I can't simply do a GROUP BY or something similar, as it will return multiple results for rows of TableA.
I'm not sure if a UDF, utilizing COALESCE or something similar might work?
Even this will serve the purpose
Sample data
declare #t table(id int, name varchar(20),somecolumn varchar(MAX))
insert into #t
select 1,'ABC','X' union all
select 1,'ABC','Y' union all
select 1,'ABC','Z' union all
select 2,'MNO','R' union all
select 2,'MNO','S'
Query:
SELECT ID,Name,
STUFF((SELECT ',' + CAST(T2.SomeColumn AS VARCHAR(MAX))
FROM #T T2 WHERE T1.id = T2.id AND T1.name = T2.name
FOR XML PATH('')),1,1,'') SOMECOLUMN
FROM #T T1
GROUP BY id,Name
Output:
ID Name SomeColumn
1 ABC X,Y,Z
2 MNO R,S
1. Create the UDF:
CREATE FUNCTION CombineValues
(
#FK_ID INT -- The foreign key from TableA which is used
-- to fetch corresponding records
)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #SomeColumnList VARCHAR(8000);
SELECT #SomeColumnList =
COALESCE(#SomeColumnList + ', ', '') + CAST(SomeColumn AS varchar(20))
FROM TableB C
WHERE C.FK_ID = #FK_ID;
RETURN
(
SELECT #SomeColumnList
)
END
2. Use in subquery:
SELECT ID, Name, dbo.CombineValues(FK_ID) FROM TableA
3. If you are using stored procedure you can do like this:
CREATE PROCEDURE GetCombinedValues
#FK_ID int
As
BEGIN
DECLARE #SomeColumnList VARCHAR(800)
SELECT #SomeColumnList =
COALESCE(#SomeColumnList + ', ', '') + CAST(SomeColumn AS varchar(20))
FROM TableB
WHERE FK_ID = #FK_ID
Select *, #SomeColumnList as SelectedIds
FROM
TableA
WHERE
FK_ID = #FK_ID
END
In MySQL there is a group_concat function that will return what you're asking for.
SELECT TableA.ID, TableA.Name, group_concat(TableB.SomeColumn)
as SomColumnGroup FROM TableA LEFT JOIN TableB ON
TableB.TableA_ID = TableA.ID
I think you are on the right track with COALESCE. See here for an example of building a comma-delimited string:
http://www.sqlteam.com/article/using-coalesce-to-build-comma-delimited-string
You may need to provide some more details for a more precise response.
Since your dataset seems kind of narrow, you might consider just using a row per result and performing the post-processing at the client.
So if you are really looking to make the server do the work return a result set like
ID Name SomeColumn
1 ABC X
1 ABC Y
1 ABC Z
2 MNO R
2 MNO S
which of course is a simple INNER JOIN on ID
Once you have the resultset back at the client, maintain a variable called CurrentName and use that as a trigger when to stop collecting SomeColumn into the useful thing you want it to do.
Assuming you only have WHERE clauses on table A create a stored procedure thus:
SELECT Id, Name From tableA WHERE ...
SELECT tableA.Id AS ParentId, Somecolumn
FROM tableA INNER JOIN tableB on TableA.Id = TableB.F_Id
WHERE ...
Then fill a DataSet ds with it. Then
ds.Relations.Add("foo", ds.Tables[0].Columns("Id"), ds.Tables[1].Columns("ParentId"));
Finally you can add a repeater in the page that puts the commas for every line
<asp:DataList ID="Subcategories" DataKeyField="ParentCatId"
DataSource='<%# Container.DataItem.CreateChildView("foo") %>' RepeatColumns="1"
RepeatDirection="Horizontal" ItemStyle-HorizontalAlign="left" ItemStyle-VerticalAlign="top"
runat="server" >
In this way you will do it client side but with only one query, passing minimal data between database and frontend
I tried the solution priyanka.sarkar mentioned and the didn't quite get it working as the OP asked. Here's the solution I ended up with:
SELECT ID,
SUBSTRING((
SELECT ',' + T2.SomeColumn
FROM #T T2
WHERE WHERE T1.id = T2.id
FOR XML PATH('')), 2, 1000000)
FROM #T T1
GROUP BY ID
Solution below:
SELECT GROUP_CONCAT(field_attr_best_weekday_value)as RAVI
FROM content_field_attr_best_weekday LEFT JOIN content_type_attraction
on content_field_attr_best_weekday.nid = content_type_attraction.nid
GROUP BY content_field_attr_best_weekday.nid
Use this, you also can change the Joins
SELECT t.ID,
t.NAME,
(SELECT t1.SOMECOLUMN
FROM TABLEB t1
WHERE t1.F_ID = T.TABLEA.ID)
FROM TABLEA t;
This will work for selecting from different table using sub query.
I have reviewed all the answers. I think in database insertion should be like:
ID Name SomeColumn
1. ABC ,X,Y Z (these are three different rows)
2. MNO ,R,S
The comma should be at previous end and do searching by like %,X,%

Resources