Combining table column in SQL Server - sql-server

I have to combine the Customer_address when they have the same address_index but different line_nbr:
CUSTOMER_NUM
CUSTOMER_CODE
CUSTOMER_ADDRSS
LINE_NBR
ADDRESS_INDEX
ADDRESS_CODE
31
GEW
3901 Castle Hayne Road
17
1
SHIP
30
GEW
Highway 117 N
18
1
SHIP
212
GEW
1005 2nd Street
19
2
SHIP
23
GEW
3901 Castle Hayne Road
20
3
SHIP
241
GEW
Highway 117 N
21
3
SHIP
360
GEW
465 McCarthy Road
1
4
SHIP
469
GEW
3901 Castle Hayne Road
1
5
SHIP
470
GEW
Highway 117 N
2
5
SHIP
I need to combine CUSTOMER_NUM 31 and 30 to read
Castle Hayne Road Highway 117 N
By combining Address_index = 1 with Line_nbr 17 and 18, and also Address_index = 3 with line_nbr 20 and 21, and Address_index 5 with line_nbr 1 and 2, etc....
New to SQL and trying to figure out how to link the two columns together and write out the customer address in one column instead of the two rows it currently resides.

On SQL Server 2017+, STRING_AGG() makes this easy:
SELECT CUSTOMER_CODE,
MergedAddress = STRING_AGG(CUSTOMER_ADDRSS, ' ')
WITHIN GROUP (ORDER BY LINE_NBR)
FROM dbo.MyTable
GROUP BY CUSTOMER_CODE, ADDRESS_INDEX;
Output:
CUSTOMER_CODE
MergedAddress
GEW
3901 Castle Hayne Road Highway 117 N
GEW
1005 2nd Street
GEW
3901 Castle Hayne Road Highway 117 N
GEW
465 McCarthy Road
GEW
3901 Castle Hayne Road Highway 117 N
On older versions, you're going to have to use more cumbersome and inefficient methods like this:
SELECT CUSTOMER_CODE,
MergedAddress = RTRIM((SELECT CUSTOMER_ADDRSS + ' '
FROM dbo.MyTable AS inside
WHERE inside.ADDRESS_INDEX = outside.ADDRESS_INDEX
ORDER BY LINE_NBR
FOR XML PATH(''), TYPE).value(N'./text()[1]', N'varchar(max)'))
FROM dbo.MyTable AS outside
GROUP BY CUSTOMER_CODE, ADDRESS_INDEX;
The example db<>fiddle shows the results and also the plans that should help explain why the latter is so bad.

Related

Subtract depletion And Limit for SUM

First I'm using AdventureWork2019 as a reference
I have a query where I'm joining 5 Tables
USE [AdventureWorks2019]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Alter PROCEDURE dbo.TestLocation
#UseDate DateTime
AS
BEGIN
SET NOCOUNT ON;
SELECT prodID
,SUM(PurchQty) AS TotalPurchase
,SUM(SalesQty) AS TotalSell
,StartDate
from (
SELECT DISTINCT WO.ProductID AS prodID
, StartDate
,WO.OrderQty AS PurchQty
,SOD.OrderQty AS SalesQty
FROM Sales.SalesOrderDetail SOD
LEFT JOIN Production.WorkOrderRouting WOR ON WOR.ProductID = SOD.ProductID
--LEFT JOIN Production.Location PL ON PL.LocationID = WOR.LocationID
--The above Join is the one for the locationID and it's working Fine
LEFT JOIN Production.WorkOrder WO ON WO.ProductID = SOD.ProductID
FULL OUTER JOIN Purchasing.PurchaseOrderDetail POD ON POD.ProductID = SOD.ProductID
WHERE StartDate = #UseDate
-- AND PL.LocationID >= 10
) Test3
Group by prodID,StartDate
order by prodID ASC, StartDate
END
GO
EXEC TestLocation '2011-07-02 00:00:00.000'
Output(sample):
prodID TotalPurc TotalSell StartDate
717 8 36 2011-07-02 00:00:00.000
730 9 47 2011-07-02 00:00:00.000
744 2 3 2011-07-02 00:00:00.000
747 12 21 2011-07-02 00:00:00.000
749 5 15 2011-07-02 00:00:00.000
761 16 138 2011-07-02 00:00:00.000
775 26 91 2011-07-02 00:00:00.000
777 12 78 2011-07-02 00:00:00.000
802 6 21 2011-07-02 00:00:00.000
804 40 60 2011-07-02 00:00:00.000
806 16 138 2011-07-02 00:00:00.000
807 24 23 2011-07-02 00:00:00.000
810 21 28 2011-07-02 00:00:00.000
811 6 21 2011-07-02 00:00:00.000
813 8 37 2011-07-02 00:00:00.000
817 21 28 2011-07-02 00:00:00.000
And another Table For LocationID (as a warehouse)
SELECT LocationID,CostRate,Availability
FROM Production.Location
WHERE LocationID >= 10
order by CostRate ASC
LocationID CostRate Availability
50 12.25 120.00
60 12.25 120.00
30 14.50 120.00
40 15.75 120.00
45 18.00 80.00
10 22.50 96.00
20 25.00 108.00
What I want to do is to take each LoactionId and ProdID and take TotalPurc to the location and decrement the quantity in the Availability column, each TotalSell will increment the Availability column. The max Availability quantity is 130.
If all locations have no Available quantity that is the Available is 0 for all locations then it will stop.
the above will work with the date specified as you can check the query and run it if you have
AdventureWork2019
simple output to check how I want the data to be:
prodID TotalPurc TotalSell StartDate
717 8 36 2011-07-02 00:00:00.000
730 9 47 2011-07-02 00:00:00.000
744 2 3 2011-07-02 00:00:00.000
747 12 21 2011-07-02 00:00:00.000
749 5 15 2011-07-02 00:00:00.000
LocationID CostRate Availability
50 12.25 120.00
60 12.25 120.00
30 14.50 120.00
40 15.75 120.00
45 18.00 80.00
10 22.50 96.00
20 25.00 108.00
Output :
prodID TotalPurc TotalSell StartDate LocationID Availability Remaining
717 8 36 2011-07-02 00:00:00.000 50 130 18
717 8 36 2011-07-02 00:00:00.000 60 130 8
717 8 36 2011-07-02 00:00:00.000 30 128 0
--what happened above is that I took the (120-8) = 112 then 112+36 = 148 we only can use 130 then the remaining is 18 then we took the next `LocationID` with the least Cost (120+18 = 138 we can use 130 so we took the 8) and used it in the next `LocationID`
730 9 47 2011-07-02 00:00:00.000 30 130 36
730 9 47 2011-07-02 00:00:00.000 40 130 26
730 9 47 2011-07-02 00:00:00.000 45 106 0
744 2 3 2011-07-02 00:00:00.000 45 107 0
747 12 21 2011-07-02 00:00:00.000 45 116 0
749 5 15 2011-07-02 00:00:00.000 45 126 0
--the above is the same as the first 3 rows we subtract and add to the availability
The other condition is that if all locations reached 0 or 130 then stop
How can I do that in SQL Server? I tried using CTE but didn't work well with me and tried the cursor which I think is the best for this kind of thing but didn't achieve anything.
Thank you in advance
Edit :
ALTER FUNCTION GetStockMovment
(
-- Add the parameters for the function here
#ForDate Datetime
)
RETURNS #Sums TABLE (
RemoveQTY Numeric(24, 7),
ADDQTY Numeric(24, 7)
)
AS
BEGIN
Declare #WoSum Numeric(24, 7),
#SODSUM Numeric(24, 7),
#WORSum Numeric(24, 7),
#PODSum Numeric(24, 7)
select #SODSUM = SUM(SOD.OrderQty) from Sales.SalesOrderDetail SOD
INNER JOIN Sales.SalesOrderHeader SOH ON SOD.SalesOrderID = SOH.SalesOrderID
where SOH.OrderDate = #ForDate
select #WoSum = sum(orderQty) from Production.WorkOrder
where StartDate = #ForDate
select #PODSum = sum(POD.OrderQty) from Purchasing.PurchaseOrderDetail POD
INNER JOIN Purchasing.PurchaseOrderHeader POH ON POD.PurchaseOrderID = POH.PurchaseOrderID
where POH.OrderDate = #ForDate
select #WoSum = sum(WO.OrderQty) from Production.WorkOrder WO
where WO.DueDate = #ForDate
INSERT INTO #Sums (RemoveQTY,ADDQTY)
SELECT isnull(#SODSUM,0) + isnull(#WORSum,0) , isnull(#PODSum,0) + isnull(#WoSum,0)
RETURN;
END;
GO
select * from dbo.GetStockMovment ('2014-05-26 00:00:00.000')
Output:
RemoveQTY ADDQTY
189.0000000 5334.0000000
You should use LAG or LEAD function.
https://learn.microsoft.com/en-us/sql/t-sql/functions/lead-transact-sql?view=sql-server-ver15
https://learn.microsoft.com/en-us/sql/t-sql/functions/lag-transact-sql?view=sql-server-ver15

Database Multiple Table Join Queries

I really don't know what queries to be combined using these table to achieve my coressponding output please assist me
Table 1
w_ref t_id q_id qty
110 111 500 1
111 112 500 2
112 113 500 3
Table 2
t_id q_id material-x material-y material_id
111 500 14 15 7
112 500 18 18 7
113 500 10 11 8
Table 3
id material_name
7 abc
8 def
I expect that joining table output like this
Output
w_ref qty material-x material-y material_name
110 1 14 15 abc
111 2 18 07 abc
112 3 10 11 def
Here is a simple example of a SQL of a INNER JOIN. This will give you a basic understanding from the results given.
SELECT a.w_ref, a.t_id, a.q_id, a.qty, b.material-x FROM table1 a INNER JOIN table2 b ON b.t_id = a.t_id;
However their is very little of what you're trying to achieve and should not just post a question asking someone to sort it. Maybe add what you have tried so far and what outcomes you have had.

compare two tables and add result in second table

I have been working on comparing two almost identical tables and update/add the result in second table. Here are the two tables i am using. Person1(ID,Name,PHNumber) and Person2(ID,Name,PHNumber,IsActive).
Note: person1 has always a more/less/same rows than person2.Person1 is reference table we need to add/delete the rows which does/doesnot present in person1 int to person2. I am using microsoft SQL server management studio.
Case1: If the compared result has more rows(Let say person1 has 10, person2 has 8rows) then we need to add those(2rows) to person2 and keep IsActive-1 for those rows.
Case2: If the compared result has differnce(Let say person1 has 10 but person2 has 20rows) then we need to find those delta 10rows and keep IsActive-0 for those 10rows.
Hope the query is clear and expecting your valuable solutions. Thank you.
Here Person1 does not contain newdata2 and contains newdata,newdata1 which are
new values after comparing, so we are giving IsActive-0 and IsActive-1
respectively.
Person1:
ID Name PHNumber
1 missouri 123
2 kansas 111
3 stlouise 234
4 california 456
5 india 888
6 srilanka 780
7 dallas 890
8 texas 1111
9 mario 1112
10 sister 7878
11 pontiac 8765
12 newdata 1234
13 newdata1 2345
Person2:
ID Name PHNumber IsActive
1 missouri 123 1
2 kansas 111 1
3 stlouise 234 1
4 california 456 1
5 india 888 1
6 srilanka 780 1
7 dallas 890 1
8 texas 1111 1
9 mario 1112 1
10 sister 7878 1
11 pontiac 8765 1
12 newdata2 987 1
RESULT: Person2:
ID Name PHNumber IsActive
1 missouri 123 1
2 kansas 111 1
3 stlouise 234 1
4 california 456 1
5 india 888 1
6 srilanka 780 1
7 dallas 890 1
8 texas 1111 1
9 mario 1112 1
10 sister 7878 1
11 pontiac 8765 1
12 newdata2 987 0
13 newdata 1234 1
14 newdata1 2345 1
If you are still looking or answer, you can try like this...
;WITH cte
AS (SELECT
COALESCE(p1.id, p2.id) AS id,
COALESCE(p1.name, p2.name) AS name,
COALESCE(p1.phnumber, p2.phnumber) AS phnumber,
CASE WHEN p1.name IS NULL THEN 0 ELSE 1 END AS IsActive
FROM Person1 p1
FULL JOIN person2 p2
ON p1.name = p2.name
AND p1.phnumber = p2.phnumber)
SELECT
ROW_NUMBER() OVER (ORDER BY id, phnumber) AS id,
name, phnumber, IsActive
FROM cte

Average value depending on cumulative value from other column aka running total

I've sifted through the various sql-server tagged threads using AVERAGE and Cumulative as search terms. Various desperate answers, but I can't cobble them together for my needs. The use case is to find the initial average value (cumulative value/cumulative days on) for a time period when cumulative days on is greater than 60 and less than 90.
Below is a table where ID identifies the object, VALUE is the amount reported on a monthly basis and DAYSON is the number of days in that month where the object ran to produce the value. YEARMONTH is date value on which on can sort.
ID VALUE DASYON YEARMONTH
1 166 27 201502
1 1 2 201505
1 569 19 201507
1 312 19 201508
2 364 27 201502
2 328 31 201503
2 242 29 201504
2 273 31 201505
2 174 30 201506
2 188 25 201507
2 203 25 201508
3 474 28 201502
3 521 31 201503
3 465 30 201504
3 473 31 201505
3 434 30 201506
3 404 31 201507
I would like to create a summary table that averages the cumulative value divided by the cumulative days uniquely for each ID where cumulative days is greater than 60 and less than 90. Below is a table that with the cumulative values. (I generated this in Excel)
ID VALUE cumValue DASYON cumDaysOn YEARMONTH
1 166 166 27 27 201502
1 1 167 2 29 201505
1 569 736 19 48 201507
1 312 1048 19 67 201508
2 364 364 27 27 201502
2 328 692 31 58 201503
2 242 934 29 87 201504
2 273 1207 31 118 201505
2 174 1381 30 148 201506
2 188 1569 25 173 201507
2 203 1772 25 198 201508
3 474 474 28 28 201502
3 521 505 31 59 201503
3 465 535 30 89 201504
3 473 566 31 120 201505
3 434 596 30 150 201506
3 404 627 31 181 201507
I try this based on other threads:
SELECT
ID,
Value,
SUM(Value) OVER (ORDER BY ID, YearMonth) [cumValue],
DaysOn,
SUM (DaysOn) OVER (Order by ID, YearMonth) as cumDaysOn,
YearMonth
FROM table
WHERE DAYSON > 0 and Liquid > 0 and YearMonth > 201501
GROUP BY ID, YearMonth, Value, DaysOn
ORDER BY ID, yearmonth
I can't get it to iterate over the ID; it just keeps summing down the column. If I could create a table or view like the one above, then I could always use a select statement and divide cumvalue by cumdayson.
Below is a table to show where I would get the initial average value (InititalAverageValue) based on the criteria:
ID VALUE cumValue DASYON cumDaysOn YEARMONTH InitalAvgValue
1 166 166 27 27 201502
1 1 167 2 29 201505
1 569 736 19 48 201507
1 312 1048 19 67 201508 55
2 364 364 27 27 201502
2 328 692 31 58 201503
2 242 934 29 87 201504 32
2 273 1207 31 118 201505
2 174 1381 30 148 201506
2 188 1569 25 173 201507
2 203 1772 25 198 201508
3 474 474 28 28 201502
3 521 505 31 59 201503
3 465 535 30 89 201504 18
3 473 566 31 120 201505
3 434 596 30 150 201506
3 404 627 31 181 201507
Ultimately what I desire is table as such:
ID InitalAvgValue
1 55
2 32
3 18
Thanks in advance for any help.
The crux is that you need a running total. There are several approaches to calculating running totals, but they have various tradeoffs between simplicity and performance. The "best" approach depends on the expected size of your data set and whether you are using SQL Server 2012 or an earlier version. The following article describes some different options along with the pros and cons:
http://sqlperformance.com/2012/07/t-sql-queries/running-totals
Here's a quick example using correlated subqueries, which may be reasonable for small data sets, but likely would not scale well to larger data:
SELECT
ID,
ROUND(AVG(CAST(CumulativeValue AS FLOAT) / CAST(CumulativeDaysOn AS FLOAT)), 1) AS Average
FROM
(
SELECT
ID,
Value,
DaysOn,
(SELECT SUM(Value) FROM ExampleTable t2 WHERE t1.ID = t2.ID and t2.YearMonth <= t1.YearMonth) AS CumulativeValue,
(SELECT SUM(DaysOn) FROM ExampleTable t2 WHERE t1.ID = t2.ID and t2.YearMonth <= t1.YearMonth) AS CumulativeDaysOn
FROM
ExampleTable t1
) AS ExampleWithTotals
WHERE
CumulativeDaysOn > 60 AND CumulativeDaysOn < 90
GROUP BY
ID
ORDER BY
ID
;
Output:
ID Average
1 15.6
2 10.7
3 16.4

need query without cross join

i have a table which has sales at day level
sales_day
loc_id day_id sales
124 2013-01-01 100
124 2013-01-02 120
124 2013-01-03 140
124 2013-01-04 160
124 2013-01-05 180
124 2013-01-06 200
124 2013-01-07 220
there is weekly table which is the aggregate of all the days
loc_id week_id sales
123 201401 1120
Now i need all of the above in table as below
loc_id day_id sales week_sales
124 2013-01-01 100 1120
124 2013-01-02 120 1120
124 2013-01-03 140 1120
124 2013-01-04 160 1120
124 2013-01-05 180 1120
124 2013-01-06 200 1120
124 2013-01-07 220 1120
there are so many loactions and so many weeks,days.
How to get the data exactly without cross join.
Have you tried this:
select loc_id, day_id, sales, week_sales
from table
cross join (
select sum(sales) as week_sales from table
) t
Window analytical function should help you here...
select loc_id,
day_id,
sales,
sum(sales) over(partition by loc_id,date_part('week', day_id)) as week_total_sales
from <table name>
It will sum the sales by location id and the week of the year to give you the total you are looking for.
In your example, 2013-01-07 was included with the other dates, but it isn't actually part of the same calendar week.
It wasn't clear which DBMS you were referring to. The above is for Netezza. For SQL Server etc try changing date_part('week',day_id) to datepart(ww,day_id).

Resources