Combining multiple rows in SQL with distinct identifier - sql-server

I'm pulling some data from SQL Server from this table.
ID_Number Date_01 Date_02 Date_03 Date_04 Date_05
---------------------------------------------------------------------
1001 6/1/2015 6/5/2015 Null Null 6/6/2015
1001 Null Null 6/5/2015 Null 6/7/2015
1002 6/20/2015 Null Null 6/21/2015 Null
1002 6/21/2015 6/22/2015 6/23/2015 6/19/2015 6/20/2015
1003 6/25/2015 Null Null 6/26/2015 6/29/2015
I'm not sure what CTE query will I use to return only one row per ID and get the max date per column for each ID.
Here's the sample result:
ID_Number Date_01 Date_02 Date_03 Date_04 Date_05
----------------------------------------------------------------------
1001 6/1/2015 6/5/2015 6/5/2015 Null 6/7/2015
1002 6/21/2015 6/22/2015 6/23/2015 6/21/2015 6/20/2015
1003 6/25/2015 Null Null 6/26/2015 6/29/2015

You don't need CTE to do this, If am not wrong simple Group by with Max aggregate should work for you
select
ID_Number,
Date_01=max(Date_01),
Date_02=max(Date_02),
Date_03=max(Date_03),
Date_04=max(Date_04),
Date_05=max(Date_05)
from yourtable
group by ID_Number

max date per column for each ID
Grouping by ID_Number :
SELECT ID_Number AS Expr1, MAX(Date_01) AS Date_01, MAX(Date_02) AS Date_02, MAX(Date_03) AS Date_03, MAX(Date_04) AS Date_04, MAX(Date_05) AS Date_05
FROM ta1
GROUP BY ID_Number

Related

GROUP BY on a column to get remove nulls

I have the following table
Order_ID Loc_ID OrderDate ShippingDate DeliveryDate
10 2 10/12/2018 null null
10 2 null null 18/12/2018
10 2 null 12/13/2019 null
Basically, every time a date is recorded, it is added as a row. I want the table to look like this:
Order_ID Loc_ID Order_Date ShippingDate DeliveryDate
10 2 10/12/2018 13/12/2018 18/12/2018
Can someone tell me how I should do this?
Use MAX:
SELECT Order_ID,
Loc_ID,
MAX(OrderDate) AS OrderDate,
MAX(ShippingDate) AS ShippingDate,
MAX(DeliveryDate) AS DeliveryDate
FROM dbo.YourTable
GROUP BY Order_ID,
Loc_ID;
When ordering data NULL has the lowest value, so any non-NULL value will have a "greater" value. As a result MAX will return the non-NULL value.
A simple aggregation should do the trick
Example
Select Order_ID
,Loc_ID
,OrderDate = max(OrderDate)
,ShippingDate = max(ShippingDate)
,DeliveryDate = max(DeliveryDate)
From YourTable
Group By Order_ID,Loc_ID

how to update foreign key

I have 2 tables; design of first table is like below:
Table 1:
id_Doc_line_sheet (pk),Autonumber
DocNo (text)
lineNo (text)
Sheet No (text)
Combination of fields (DocNo, lineNo, Sheet No) is index and unique.
Design of second table is like below:
Table 2:
id_Doc_line_trans (pk), Autonumber
id_Doc_line_sheet (fk),Number
name
Now in table1, for the field lineNo I have records with leading spaces and without leading spaces like below:
id_Doc_line_sheet DocNo lineNo Sheet No
------------------------------------------------------------------
1001 doc-0001 line-0001 1
1002 doc-0001 line-0001 1
1003 doc-0001 line-0001 2
1004 doc-0001 line-0001 2
1005 doc-0002 line-0002 1
1006 doc-0002 line-0002 1
1007 doc-0001 line-0005 1
1008 doc-0001 line-0005 1
And I want deleted these records with leading spaces but at first I want to update (id_Doc_line_sheet) for each unique (DocNo,lineNo,Sheetno) to correct one (without leading space).
I mean if the table2 is like below:
id_Doc_line_trans id_Doc_line_sheet name
---------------------------------------------------
1 1001 name01
2 1002 name02
3 1003 name03
4 1004 name04
5 1007 name07
6 1008 name08
I update (id_Doc_line_sheet) to something like below:
id_Doc_line_trans id_Doc_line_sheet name
---------------------------------------------------
1 1001 name01
2 1001 name02
3 1003 name03
4 1003 name04
5 1007 name07
6 1007 name08
And then delete records with leading spaces from first table.
Please help how I can do it?
Based on what I understood, it seems you want to update the table2 based on the sheet_no first id used. If so, you can use the following:
Explanation
You need to get the first record in table1 based on the sheet_no using report cte
Find the corresponding sheet_no for each row in table2
Update the table2 entries where row_number =1
Query
;with report as(
select row_number() over(partition by sheet_no order by id_doc_line_sheet) as [Row],id_doc_line_sheet,sheet_no
from table1
where line_no not like ' %' -- here you can ensure that lin_no doesn't start with leading space
), combined as(
select t2.id_doc_line_trans,
t2.id_doc_line_sheet,
t1.sheet_no
from table2 t2
inner join table1 t1 on t2.id_doc_line_sheet = t1.id_doc_line_sheet
)
update t set t.id_doc_line_sheet = r.id_doc_line_sheet
from report r
inner join combined c on r.sheet_no = c.sheet_no
inner join table2 t on t.id_doc_line_trans = c.id_doc_line_trans
where r.[Row]=1
result after updating table2
id_doc_line_trans id_doc_line_sheet name
1 1001 name01
2 1001 name02
3 1003 name03
4 1003 name04
Then you can apply the delete statement based on your requirements
Here a working demo
Hope this will help you
Here is an approach which does not use analytic functions, which was the easiest way to approach this in my mind. We can aggregate over table1 and pivot out both the id of the records both with and without leading whitespace. Also, we can check to make sure a given document/sheet even has such a page occurring. Then, all we need to do is join table2 to this first CTE to get the old and new id values in a single record.
WITH cte1 AS (
SELECT
DocNo, [Sheet No],
MAX(CASE WHEN [lineNo] LIKE ' %' THEN id_Doc_line_sheet END) AS id_old,
MAX(CASE WHEN [lineNo] NOT LIKE ' %' THEN id_Doc_line_sheet END) AS id_new
FROM table1
GROUP BY DocNo, [Sheet No]
HAVING SUM(CASE WHEN [lineNo] LIKE ' %' THEN 1 ELSE 0 END) > 0
),
cte2 AS (
SELECT
t1.id_Doc_line_trans, t1.id_Doc_line_sheet, t1.name, t2.id_old, t2.id_new
FROM table2 t1
INNER JOIN cte1 t2
ON t1.id_Doc_line_sheet = t2.id_old
)
UPDATE cte2
SET id_Doc_line_sheet = id_new;
Note that the update logic is trivial; all the information and records of interest were already included in the second CTE.
Demo

Insert history into a new table

I'm working on this table as shown below it has name, date,id and key. I would like to insert into new table where the old_name column maintains the change in name for that key. The output result is shown below as well. Thanks
id name date Key
1 charles 2004-05-07 1001
2 CON 2004-05-07 1001
3 Virginia 2006-09-08 1001
4 MART 2012-01-03 1001
5 McDonalds 2013-12-30 1001
OUTPUT
id old_name name date Key
1 NULL charles 2004-05-07 1001
2 charles CON 2004-05-07 1001
3 CON Virginia 2006-09-08 1001
4 Virginia MART 2012-01-03 1001
5 MART McDonalds 2013-12-30 1001
Seems like a good fit for LAG()
Select id
,old_name = lag(name,1) over (Partition By [Key] Order by ID)
,name
,date
,[key]
From YourTable
Order By ID
You can use lag if it is SQL Server >=2012
select *, lag(name,1,null) over(order by id) Old_name from #yourhistory
Your input table
create table #yourhistory (id int, name varchar(20), date date, [key] int)
insert into #yourhistory
( id , name , date , [Key] ) values
( 1 ,'charles ','2004-05-07', 1001 )
,( 2 ,'CON ','2004-05-07', 1001 )
,( 3 ,'Virginia ','2006-09-08', 1001 )
,( 4 ,'MART ','2012-01-03', 1001 )
,( 5 ,'McDonalds','2013-12-30', 1001 )

sql server 2012 merge values from two tables

I have two tables
tblA(sn, ID int pk, name varchar(50), amountA decimal(18,2))
and
tblB(ID int fk, amountB decimal(18,2))
here: tblA occures only once and tblB may occure multiple time
I need the query to display data like:
sn ID name AmountA amountB Balance
1 1001 abc 5000.00 5000.00
2 1002 xyz 10000.00
1002 4000.00 6000.00 (AmountA-AmountB)
3 1003 pqr 15000.00
1003 4000.00
1003 3000.00
1003 2000.00 6000.00 (AmountA-sum(AmountB))
Please ask if any confusion
I tried using lag and lead function but I couldnot get the desire result, Please help.
Since you are using SQL Server 2012, you can use a partition with an aggregate function (SUM):
SELECT t.sn,
t.ID,
t.name,
t.credits AS AmountA,
t.debits AS amountB,
SUM(t.credits - t.debits) OVER (PARTITION BY t.ID ORDER BY t.debits, t.credits) AS Balance
FROM
(
SELECT sn,
ID,
name,
AmountA AS credits,
0 AS debits
FROM tblA
UNION ALL
SELECT 0 AS sn,
ID,
NULL AS name,
0 AS credits,
amountB AS debits
FROM tblB
) t
ORDER BY t.ID,
t.debits,
t.credits
Explanation:
Since the records in tables A and B each represent a single transaction (i.e. a credit or debit), using a UNION query to bring both sets of data into a single table works well here. After this, I compute a rolling sum using the difference between credit and debit, for each record, for each ID partition group. The ordering is chosen such that credits appear at the top of each partition while debits appear on the bottom.

replace NULL values with latest non-NULL value in resultset series (SQL Server 2008 R2)

for SQL Server 2008 R2
I have a resultset that looks like this (note [price] is numeric, NULL below represents a
NULL value, the result set is ordered by product_id and timestamp)
product timestamp price
------- ---------------- -----
5678 2008-01-01 12:00 12.34
5678 2008-01-01 12:01 NULL
5678 2008-01-01 12:02 NULL
5678 2008-01-01 12:03 23.45
5678 2008-01-01 12:04 NULL
I want to transform that to a result set that (essentially) copies a non-null value from the latest preceding row, to produce a resultset that looks like this:
product timestamp price
------- ---------------- -----
5678 2008-01-01 12:00 12.34
5678 2008-01-01 12:01 12.34
5678 2008-01-01 12:02 12.34
5678 2008-01-01 12:03 23.45
5678 2008-01-01 12:04 23.45
I don't find any aggregate/windowing function that will allow me to do this (again this ONLY needed for SQL Server 2008 R2.)
I was hoping to find an analytic aggregate function that do this for me, something like...
LAST_VALUE(price) OVER (PARTITION BY product_id ORDER BY timestamp)
But I don't seem to find any way to do a "cumulative latest non-null value" in the window (to bound the window to the preceding rows, rather than the entire partition)
Aside from creating a table-valued user defined function, is there any builtin that would accomplish this?
UPDATE:
Apparently, this functionality is available in the 'Denali' CTP, but not in SQL Server 2008 R2.
LAST_VALUE http://msdn.microsoft.com/en-us/library/hh231517%28v=SQL.110%29.aspx
I just expected it to be available in SQL Server 2008. It's available in Oracle (since 10gR2 at least), and I can do something similar in MySQL 5.1, using a local variable.
http://download.oracle.com/docs/cd/E14072_01/server.112/e10592/functions083.htm
You can try the following:
* Updated **
-- Test Data
DECLARE #YourTable TABLE(Product INT, Timestamp DATETIME, Price NUMERIC(16,4))
INSERT INTO #YourTable
SELECT 5678, '20080101 12:00:00', 12.34
UNION ALL
SELECT 5678, '20080101 12:01:00', NULL
UNION ALL
SELECT 5678, '20080101 12:02:00', NULL
UNION ALL
SELECT 5678, '20080101 12:03:00', 23.45
UNION ALL
SELECT 5678, '20080101 12:04:00', NULL
;WITH CTE AS
(
SELECT *
FROM #YourTable
)
-- Query
SELECT A.Product, A.Timestamp, ISNULL(A.Price,B.Price) Price
FROM CTE A
OUTER APPLY ( SELECT TOP 1 *
FROM CTE
WHERE Product = A.Product AND Timestamp < A.Timestamp
AND Price IS NOT NULL
ORDER BY Product, Timestamp DESC) B
--Results
Product Timestamp Price
5678 2008-01-01 12:00:00.000 12.3400
5678 2008-01-01 12:01:00.000 12.3400
5678 2008-01-01 12:02:00.000 12.3400
5678 2008-01-01 12:03:00.000 23.4500
5678 2008-01-01 12:04:00.000 23.4500
I have a table containing the following data. I want to update all nulls in salary columns with previous value without taking null value.
Table:
id name salary
1 A 4000
2 B
3 C
4 C
5 D 2000
6 E
7 E
8 F 1000
9 G 2000
10 G 3000
11 G 5000
12 G
here is the query that works for me.
select a.*,first_value(a.salary)over(partition by a.value order by a.id) as abc from
(
select *,sum(case when salary is null then 0 else 1 end)over(order by id) as value from test)a
output:
id name salary Value abc
1 A 4000 1 4000
2 B 1 4000
3 C 1 4000
4 C 1 4000
5 D 2000 2 2000
6 E 2 2000
7 E 2 2000
8 F 1000 3 1000
9 G 2000 4 2000
10 G 3000 5 3000
11 G 5000 6 5000
12 G 6 5000
Try this:
;WITH SortedData AS
(
SELECT
ProductID, TimeStamp, Price,
ROW_NUMBER() OVER(PARTITION BY ProductID ORDER BY TimeStamp DESC) AS 'RowNum'
FROM dbo.YourTable
)
UPDATE SortedData
SET Price = (SELECT TOP 1 Price
FROM SortedData sd2
WHERE sd2.RowNum > SortedData.RowNum
AND sd2.Price IS NOT NULL)
WHERE
SortedData.Price IS NULL
Basically, the CTE creates a list sorted by timestamp (descending) - the newest first. Whenever a NULL is found, the next row that contains a NOT NULL price will be found and that value is used to update the row with the NULL price.

Resources