Remove and Track Items Bought and Returned - sql-server

I have a table which records all the transaction details for items. The format looks like the following columns:
Item
Transaction Type (Sale, Purchase, Vendor Return,Sales Return)
Change Description (detail along with OrderNumber)
Quantity Added
Quantity Subtracted
I am trying to get the Last Purchase Date, and the Last Sale Date which was not returned. For example a purchase was made on OrderNumber 1256 on May 15, 2015 for 144 Items and the next day there is a Transaction for a return on OrderNumber 1256. I'm not sure how I can approach this. Would I create another column which has Returned (True or False) and then update that depending on if there was a return associated to this?
Would this be achieved via stored procedure or a simple query?
Here is the sample data:
Item TransactionDate TransactionType ChangeDescription AdditionQty SubtractionQty
1006 2015-05-27 VENDOR RETURN RETURN NO. 423 0 -144
1006 2015-05-28 PURCHASE PURCH NO. 423 144 0
So When I am pulling up the Last Purchase Date, this would essentially be removed because this was purchased but returned.

If I correctly understood and you have an order number in item, this could be what you are looking for; of course with more info it could be easier ...
SELECT Item, LastPurchaseDate
FROM (
SELECT Item, MAX(TransactionDate) LastPurchaseDate, SUM(AdditionQty) AdditionQty, SUM(SubtractionQty) SubtractionQty
FROM Table
GROUP BY Item ) A
WHERE (A.AdditionQty + A.SubtractionQty) != 0

Using a temp table:
select Item, TransactionDate, LEFT(ChangeDescription,3) as TransType, RIGHT(a.ChangeDescription,LEN(a.ChangeDescription)-(CHARINDEX('.',a.ChangeDescription) + 1) as OrderNo
into #temp
from [table]
SELECT a.Item,MAX(a.TransactionDate),a.TransType,a.OrderNo
FROM #temp a
INNER JOIN #temp b
ON a.OrderNo = b.OrderNo AND a.TransType <> b.TransType
GROUP BY a.TransType

Related

SQL Server : update every 5 records with Past Months

I want to update 15 records in that first 5 records date should be June 2019,next 5 records with July 2019,last 5 records with Aug 2019 based on employee id,Can any one tell me how to write this type of query in SQL Server Management Studio V 17.7,I've tried with below query but unable to do for next 5 rows..
Like below query
Update TOP(5) emp.employee(nolock) set statusDate=GETDATE()-31 where EMPLOYEEID='XCXXXXXX';
To update only a certain number of rows of a table you will need to include a FROM clause and join a sub-query which limits the number of rows. I would suggest using OFFSET AND FETCH instead of top so that you can skip X number of rows
You will also want to use the DATEADD function instead of directly subtracting a number from the DateTime function GETDATE(). I'm not certain but I think your query will subtract milliseconds. If you intend to go back a month I would suggest subtracting a month rather than 31 days. Alternatively it might be easier to specify an exact date like '2019-06-01'
For example:
TableA
- TableAID INT PK
- EmployeeID INT FK
- statusDate DATETIME
UPDATE TableA
SET statusDate = '2019-06-01'
FROM TableA
INNER JOIN
(
SELECT TableAID
FROM TableA
WHERE EmployeeID = ''
ORDER BY TableAID
OFFSET 0 ROWS
FETCH NEXT 5 ROWS ONLY
) T1 ON TableA.TableAID = T1.TableAID
Right now it looks like your original query is updating the table employee rather than a purchases table. You will want to replace my TableA with whichever table it is you're updating and replace TableAID with the PK field of it.
You can use a ROW_NUMBER to get a ranking by employee, then just update the first 15 rows.
;WITH EmployeeRowsWithRowNumbers AS
(
SELECT
T.*,
RowNumberByEmployee = ROW_NUMBER() OVER (
PARTITION BY
T.EmployeeID -- Generate a ranking by each different EmployeeID
ORDER BY
(SELECT NULL)) -- ... in no particular order (you should supply one if you have an ordering column)
FROM
emp.employee AS T
)
UPDATE E SET
statusDate = CASE
WHEN E.RowNumberByEmployee <= 5 THEN '2019-06-01'
WHEN E.RowNumberByEmployee BETWEEN 6 AND 10 THEN '2019-07-01'
ELSE '2019-08-01' END
FROM
EmployeeRowsWithRowNumbers AS E
WHERE
E.RowNumberByEmployee <= 15

TSQL to choose a record that meets the criteria or first one

I have a table for company phone numbers and one of the columns is IsPrimary which is a boolean type. The table looks like this:
CompanyId | AreaCode | PhoneNumber | IsPrimary
123 212 555-1212 0
234 307 555-1234 1
234 307 555-4321 0
As you can see in the first record, even though the phone number is the only one for CompanyId: 123, it's not marked as the primary.
In such cases, I want my SELECT statement to return the first available number for that company.
My current SELECT statement looks like this which does NOT return a number unless it's set as the primary number.
SELECT *
FROM CompanyPhoneNumbers AS t
WHERE t.IsPrimary = 1
How can I modify this SELECT statement so that it includes the phone number for CompanyId: 123?
The query might be different depending on what you are actually up to.
If you already have the CompanyId and only need the phone number for it, that's easy:
select top (1) pn.*
from dbo.CompanyPhoneNumbers pn
where pn.CompanyId = #CompanyId -- A parameter provided externally, by calling code for instance
order by pn.IsPrimary desc;
However, if you need all companies' data, including one of their phones (for example, you might be going to create a view for this), then you need a correlated subquery:
select c.*, oa.*
from dbo.Companies c
outer apply (
select top (1) pn.*
from dbo.CompanyPhoneNumbers pn
where pn.CompanyId = c.Id
order by pn.IsPrimary desc
) oa;
I have deliberately used outer instead of cross apply, otherwise it will filter out companies with no phone numbers listed.
You can achieve this using an apply statement. This looks at the exact same table and returns the record with the highest IsPrimary so, this would return the records with a 1 in that column. If there are more than one marked as primary or not as primary, then it returns the phone number, with area code, in ascending order.
select b.*
from CompanyPhoneNumbers a
cross apply (
select top 1
*
from CompanyPhoneNumbers b
where b.CompanyId = a.CompanyId
order by b.IsPrimary desc
,b.AreaCode
,b.PhoneNumber
) b

Printing the current value and previous value between the date range

I have a sample data like this
ID DATE TIME STATUS
---------------------------------------------
A 01-01-2000 0900 ACTIVE
A 05-02-2000 1000 INACTIVE
A 01-07-2000 1300 ACTIVE
B 01-05-2005 1000 ACTIVE
B 01-08-2007 1050 ACTIVE
C 01-01-2010 0900 ACTIVE
C 01-07-2010 1900 INACTIVE
From the above data set, if we only focus on ID='A' we note that A was initally active, then became inactive on 05-02-2000 and then it was inactive until 01-07-2000.
Which means that A was inactive from 05-Feb-2000 to 01-July-2000.
My questions are:
if I execute a query with (ID=A, Date=01-04-2000) it should give me
A 05-02-2000 1000 INACTIVE
because since that date is not available in that data set, it should search for the previous one and print that
Also, if my condition is (ID=A, Date=01-07-2000) it should not only print the value which is present in the table, but also print a previous value
A 05-02-2000 1000 INACTIVE
A 01-07-2000 1300 ACTIVE
I would really appreciate if any one can assist me solve this query. I am trying my best to solve this.
Thank you every one.
Any take on this?
Afaq
Something like the following should work:
SELECT ID, Date, Time, Status
from (select ID, Date, Time, Status, row_number() over (order by Date) Ranking
from MyTable
where ID = #SearchId
and Date <= #SearchDate) xx
where Ranking < 3
order by Date, Time
This will return at most two rows. Its not clear if you are using Date and Time datatyped columns, or if you are actually using reserved words as column names, so you'll have to fuss with that. (I left out Time, but you could easily add that to the various orderings and filterings.)
Given the revised criteria, it gets a bit trickier, as the inclusion or exclusion of a row depends upon the value returned in a different row. Here, the “second” row, if there are two or more rows, is included only if the “first” row equals a particular value. The standard way to do this is to query the data to get the max value, then query it again while referencing the result of the first set.
However, you can do a lot of screwy things with row_number. Work on this:
SELECT ID, Date, Time, Status
from (select
ID, Date, Time, Status
,row_number() over (partition by case when Date = #SearchDate then 0 else 1 end
order by case when Date = #SearchDate then 0 else 1 end
,Date) Ranking
from MyTable
where ID = #SearchId
and Date <= #SearchDate) xx
where Ranking = 1
order by Date, Time
You'll have to resolve the date/time issue, since this only works against dates.
Basically you need to pull a row if, for the specified date, it is:
1) the last record, or
2) the last inactive record.
And the two conditions may match the same row as well as two distinct rows.
Here's how this logic could be implemented in SQL Server 2005+:
WITH ranked AS (
SELECT
ID,
Date,
Time,
Status,
RankOverall = ROW_NUMBER() OVER ( ORDER BY Date DESC),
RankByStatus = ROW_NUMBER() OVER (PARTITION BY Status ORDER BY Date DESC)
FROM Activity
WHERE ID = #ID
AND Date <= #Date
)
SELECT
ID,
Date,
Time,
Status,
FROM ranked
WHERE RankOverall = 1
OR Status = 'INACTIVE' AND RankByStatus = 1

Include NULL in each "Details Group" in SSRS

In SSRS I have a List with, say, a table with two columns: name and number e.g.:
NAME NUMBER
John 123
John 456
John NULL
Name is never null, but number may be. In this case I want the report to include the NULL in each group, like this:
GROUP 1:
John NULL
John 123
GROUP 2:
John NULL
John 456
The SSRS, however, puts the null in a group on its own. How do I accomplish this?
You have told SSRS to group on the NUMBER column, so it will generate a separate group for each value in the NUMBER column and then display those rows. To get what you want, you have to make the data set have the rows you want.
Select Name, Number, cast(Number as varchar(50)) as displayvalue
From mytable
UNION ALL
Select m.Name, m.Number, 'NULL' as displayvalue
From mytable m
Where exists(Select 1 from mytable where Name=m.Name and Number is NULL)
Group by Name, Number
Then group on the Number column but report on the DisplayValue column.

how to create a mssql view for getting last state information

lets say, i have two tables, one for object records and one for activity records about these objects.
i'm inserting a new record in this activity table every time an object is inserted or updated.
for telling it in a simple way, assume i have four fields in activity table; objectId, type, status and date.
when an object is about to be updated, i'm planning to get the last state for the object and look for the changes. if there is a difference between the updating value and the previous value, i'll set the value with new input, otherwise i'll set it null. so for example in an update process, user only changes the status value of the object but leaves the type value as the same, so i'll insert a new row with a null value for type and a new value for the status.
SELECT * FROM Activity;
oid type status date
-----------------------------------------
1 0 1 2009.03.05 17:58:07
1 null 2 2009.03.06 07:00:00
1 1 null 2009.03.07 20:18:07
1 3 null 2009.03.08 07:00:00
so i have to create a view tells me the current state of my object like,
SELECT * FROM ObjectStateView Where oid = 1;
oid type status date
-----------------------------------------
1 3 2 2009.03.08 07:00:00
how do i achieve this_?
Assuming date can be used to find latest record:
CREATE VIEW foo
AS
SELECT
A.oid,
(SELECT TOP 1 type FROM Activity At WHERE At.OID = A.oid AND At.Date <= MAX(A.date) AND type IS NOT NULL),
(SELECT TOP 1 status FROM Activity Ast WHERE Ast.OID = A.oid AND Ast.Date <= MAX(A.date) AND status IS NOT NULL),
MAX(A.date) AS date
FROM
Activity A
GO
Edit: if you want a JOIN (untested)
CREATE VIEW foo
AS
SELECT TOP 1
A.oid,
At.type,
Ast.status,
A.date
FROM
Activity A
LEFT JOIN
(SELECT TOP 1 oid, date, type FROM Activity WHERE type IS NOT NULL ORDER BY date DESC) At ON A.OID = At.oid
LEFT JOIN
(SELECT TOP 1 oid, date, status FROM Activity WHERE status IS NOT NULL ORDER BY date DESC) Ast ON A.OID = Ast.oid
ORDER BY date DESC
GO
Should have added this earlier:
It will scale exponentially because you have to touch the table 11 different times.
A better solution would be to maintain a "current" table and maintain it via a trigger on activity.
Have you considered using MAX function?
select oid, type, status, MAX(date) as max_date
from ObjectStateView
where oid = 1
Not really sure why you'd want the nulls in there. You can track what's changed between inputs by comparing the latest entry to the previous. Then the current state of the object is the latest entry in the table. You can determine if an object has changed by creating a hash of the parts of the object that you want to track changes to and storing that as an extra column.
Historical values:
Since you track changes, you may want to see the status of the object historically:
SELECT a.oid,
a.date,
a_type.type,
a_status.status
FROM Activity a
LEFT JOIN Activity a_type
ON a_type.oid = a.oid
AND a_type.date = (SELECT TOP 1 date FROM Activity WHERE oid = a.oid AND date <= a.date AND type IS NOT NULL ORDER BY date DESC)
LEFT JOIN Activity a_status
ON a_status.oid = a.oid
AND a_status.date = (SELECT TOP 1 date FROM Activity where oid = a.oid AND date <= a.date AND status IS NOT NULL ORDER BY date DESC)
which will return:
oid date type status
----------- ---------- ----------- -----------
1 2009-03-05 0 1
1 2009-03-06 0 2
1 2009-03-07 1 2
1 2009-03-08 3 2
Performance consideration:
On the other hand, if you have more then just a few fields, and the table is big, the performance would become an issue. In this case I would make sense also to store/cache the whole values in another table MyDataHistory, which would contain data like in the table shown above. Then selecting the current(latest) version is trivial using a SQL view filtering the latest row (1 row only) by oid and date.

Resources