I need some advice.
I have a column with nulls in a field at a particular time period.
Name Date
----- -----
Cat 1/1/2012
Dog 1/2/2012
Fish 1/3/2012
NULL 1/4/2012
Goat 1/5/2012
NULL 1/6/2012
Sheep 1/7/2012
In the example above, how would I write a SQL statement to select the last known value for column Name whenever there is a null value in the column?
For instance I'd like the table to look like this:
Name Date
----- -----
Cat 1/1/2012
Dog 1/2/2012
Fish 1/3/2012
Fish 1/4/2012
Goat 1/5/2012
Goat 1/6/2012
Sheep 1/7/2012
Would this pseudo-code work?
Select
isnull(Name, "Select Name from Table where Date = GetDate() - 1")
,Date
From Table
Try this [v is original, v2 is calculated new value] :
DECLARE #table table(v varchar(10), d varchar(10))
INSERT INTO #table
SELECT 'Cat','1/1/2012' UNION
SELECT 'Dog','1/2/2012' UNION
SELECT 'Fish','1/3/2012' UNION
SELECT NULL,'1/4/2012' UNION
SELECT 'Goat','1/5/2012' UNION
SELECT NULL,'1/6/2012' UNION
SELECT 'Sheep','1/7/2012'
SELECT A.v, A.d, ISNULL(A.v,B.v) V2
FROM #table A
OUTER APPLY ( SELECT TOP 1 *
FROM #table
WHERE d < A.d
AND v IS NOT NULL
ORDER BY d desc) B
order by d
Related
I have a a table Orders:
select * Orders order by 2
Ordernumber Zone
12345 1
12345 2
12345 3
What sql would produce the following output?
Ordernumber Zone
12345 123
Unfortunately, LIST cannot be used, since this is an old Firebird 1.5xxx.
Are there any other possibilities?
CREATE PROCEDURE GET_ZONER(ordernumber Integer)
returns (zoner varchar(20))
AS
declare variable zone varchar(20) ;
Begin
zoner = '';
for
select distinct zone from orders
where ordernumber = :ordernumber order by zone into :zone do
begin
zoner = zoner || :zone;
end
SUSPEND;
End
and then use
select * from getzoner(1234)
Database Records:
I want to represent as record set using stored procedure.
I have many records likewise in MS SQL DataBase.
It will be Listing record groupby A, B, C .. Z wize..
Automatically insert the Alphabets while got output from SQL Table.
I want below output from procedure..
How it will possible using Stored Procedure..?
You can use LEFT and UNION for this, though you will still get a 3 columns row for the rows that contains only the first letter:
Create and populate sample table (Please save us this step in your future questions)
DECLARE #T as TABLE
(
Name varchar(20),
Location varchar(20),
CreatedOn date
)
INSERT INTO #T VALUES
('Alex macwan', 'New york', '2015-12-10'),
('Jone Dinee', 'Denmark', '2016-05-01'),
('Jolly llb', 'USA', '2016-01-02'),
('Amin Mark', 'India', '2015-01-08'),
('Ben Denis', 'Brazil', '2015-10-02')
The query:
SELECT Name, Location, CreatedOn
FROM #T
UNION
SELECT LEFT(Name, 1), NULL, NULL
FROM #T
ORDER BY Name
Results:
Name Location CreatedOn
-------------------- -------------------- ----------
A NULL NULL
Alex macwan New york 2015-12-10
Amin Mark India 2015-01-08
B NULL NULL
Ben Denis Brazil 2015-10-02
J NULL NULL
Jolly llb USA 2016-01-02
Jone Dinee Denmark 2016-05-01
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
I'm working with SQL Server 2005 and looking to export some data off of a table I have. However, prior to do that I need to update a status column based upon a field called "VisitNumber", which can contain multiple entries same value entries. I have a table set up in the following manner. There are more columns to it, but I am just putting in what's relevant to my issue
ID Name MyReport VisitNumber DateTimeStamp Status
-- --------- -------- ----------- ----------------------- ------
1 Test John Test123 123 2014-01-01 05.00.00.000
2 Test John Test456 123 2014-01-01 07.00.00.000
3 Test Sue Test123 555 2014-01-02 08.00.00.000
4 Test Ann Test123 888 2014-01-02 09.00.00.000
5 Test Ann Test456 888 2014-01-02 10.00.00.000
6 Test Ann Test789 888 2014-01-02 11.00.00.000
Field Notes
ID column is a unique ID in incremental numbers
MyReport is a text value and can actually be thousands of characters. Shortened for simplicity. In my scenario the text would be completely different
Rest of fields are varchar
My Goal
I need to address putting in a status of "F" for two conditions:
* If there is only one VisitNumber, update the status column of "F"
* If there is more than one visit number, only put "F" for the one based upon the earliest timestamp. For the other ones, put in a status of "A"
So going back to my table, here is the expectation
ID Name MyReport VisitNumber DateTimeStamp Status
-- --------- -------- ----------- ----------------------- ------
1 Test John Test123 123 2014-01-01 05.00.00.000 F
2 Test John Test456 123 2014-01-01 07.00.00.000 A
3 Test Sue Test123 555 2014-01-02 08.00.00.000 F
4 Test Ann Test123 888 2014-01-02 09.00.00.000 F
5 Test Ann Test456 888 2014-01-02 10.00.00.000 A
6 Test Ann Test789 888 2014-01-02 11.00.00.000 A
I was thinking I could handle this by splitting each types of duplicates/triplicates+ (2,3,4,5). Then updating every other (or every 3,4,5 rows). Then delete those from the original table and combine them together to export the data in SSIS. But I am thinking there is a much more efficient way of handling it.
Any thoughts? I can accomplish this by updating the table directly in SQL for this status column and then export normally through SSIS. Or if there is some way I can manipulate the column for the exact conditions I need, I can do it all in SSIS. I am just not sure how to proceed with this.
WITH cte AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY VisitNumber ORDER BY DateTimeStamp) rn from MyTable
)
UPDATE cte
SET [status] = (CASE WHEN rn = 1 THEN 'F' ELSE 'A' END)
I put together a test script to check the results. For your purposes, use the update statements and replace the temp table with your table name.
create table #temp1 (id int, [name] varchar(50), myreport varchar(50), visitnumber varchar(50), dts datetime, [status] varchar(1))
insert into #temp1 (id,[name],myreport,visitnumber, dts) values (1,'Test John','Test123','123','2014-01-01 05:00')
insert into #temp1 (id,[name],myreport,visitnumber, dts) values (2,'Test John','Test456','123','2014-01-01 07:00')
insert into #temp1 (id,[name],myreport,visitnumber, dts) values (3,'Test Sue','Test123','555','2014-01-01 08:00')
insert into #temp1 (id,[name],myreport,visitnumber, dts) values (4,'Test Ann','Test123','888','2014-01-01 09:00')
insert into #temp1 (id,[name],myreport,visitnumber, dts) values (5,'Test Ann','Test456','888','2014-01-01 10:00')
insert into #temp1 (id,[name],myreport,visitnumber, dts) values (6,'Test Ann','Test789','888','2014-01-01 11:00')
select * from #temp1;
update #temp1 set status = 'F'
where id in (
select id from #temp1 t1
join (select min(dts) as mindts, visitnumber
from #temp1
group by visitNumber) t2
on t1.visitnumber = t2.visitnumber
and t1.dts = t2.mindts)
update #temp1 set status = 'A'
where id not in (
select id from #temp1 t1
join (select min(dts) as mindts, visitnumber
from #temp1
group by visitNumber) t2
on t1.visitnumber = t2.visitnumber
and t1.dts = t2.mindts)
select * from #temp1;
drop table #temp1
Hope this helps
I have a temp table variable with a bunch of columns:
Declare #GearTemp table
(
ItemNumber varchar(20),
VendorNumber varchar(6),
ItemStatus varchar(20),
Style varchar(20),
ItemName varchar(100),
ItemDescription varchar(1000),
Color varchar(50),
[Size] varchar(50),
ItemCost decimal(9,4),
IsQuickShipFl bit,
IsEmbroiderable bit,
IsBackOrderable bit,
LoadDate smalldatetime
)
It gets filled with data from another table via an insert statement, and I want to take that data and update my Products table. If possible, I would like to do something like this:
Update Products blah blah blah all columns where itemnumbers match up
SELECT * FROM #GearTemp FT
WHERE EXISTS (SELECT P.ItemNumber FROM Products P WHERE FT.ItemNumber = P.ItemNumber)
Is that possible to do? If it's not, please point me in the right direction.
If I understand you correctly, you can use something like this:
UPDATE p SET X = gt.X, Y = gt.Y -- etc... (not sure whether your column names match up)
FROM Products p
INNER JOIN #GearTemp gt ON p.ItemNumber = gt.ItemNumber
Note that this will only work if, as you stated in the comments above, there is only ever one entry in #GearTemp for each ItemNumber.
Since you are working on SQL Server 2008 you can use the MERGE statement:
-- Target Table
DECLARE #tgt TABLE (OrdID INT, ItemID INT, Qty INT, Price MONEY);
INSERT INTO #tgt (OrdID, ItemID, Qty, Price)
SELECT 1, 100, 10, 10.00 UNION ALL
SELECT 1, 101, 10, 12.00
OrdID ItemID Qty Price
----------- ----------- ----------- ---------------------
1 100 10 10.00
1 101 10 12.00
-- Source Table
DECLARE #src TABLE (OrdID INT, ItemID INT, Qty INT, Price MONEY);
INSERT INTO #src (OrdID, ItemID, Qty, Price)
SELECT 1, 100, 12, 10.00 UNION ALL
SELECT 1, 102, 10, 12.00 UNION ALL
SELECT 1, 103, 5, 7.00
OrdID ItemID Qty Price
----------- ----------- ----------- ---------------------
1 100 12 13.00
1 102 10 12.00
1 103 5 7.00
MERGE #tgt AS t
USING #src AS s
ON t.OrdID = s.OrdID AND t.ItemID = s.ItemID
WHEN MATCHED THEN
UPDATE SET
t.Qty = s.Qty,
t.Price = s.Price;
Content of the target table after the MERGE operation:
OrdID ItemID Qty Price
----------- ----------- ----------- ---------------------
1 100 12 13.00