How to delete one/multiple row/s in two tables based on one table in SQL Server - sql-server

I have two tables (to make things easier I will copy everything here)
that I need to delete from.
The main table to delete from is done based on another table, which this I could do as below
select
items.item, items.ORDERNO, items.LU_ON,
stock.NUMBER, stock.CUSTOMTEXT, stock.LU_ON
from
items, stock
where
items.item = stock.NUMBER
and items.LU_ON >= '2021-11-10'
and convert(VARCHAR, STOCK.CUSTOMTEXT) = 'breakout'
and convert(VARCHAR, ORDERNO) <> ''
delete from items
where items.item in (select stock.number
from stock
where items.item = stock.NUMBER
and items.LU_ON >= '2021-11-10'
and convert(VARCHAR, ORDERNO) <> ''
and convert(VARCHAR, STOCK.CUSTOMTEXT) = 'breakout')
However in order for this to fully work in SQL without touching the program on the front end i also need to delete the box (from the item which was deleted) from the box table based on the single item in the order which was deleted in the items table. Below are some columns/rows in the items table, i need to delete the first item, and that is based on this item being in the "stock" table which has the 'item' from the items table, just in the stock table 'item' is 'number' and i will delete from the items table any time the lu_on column (in both the stock and items table) has the date which i put in, and also has the word 'breakout' in the 'customtext' column from the stock table.
ORDERNO ITEM_ID ITEM
-------------------------------
1225971 4208453 HRE-IK.S
1225971 4208454 HRE-IK.B1
1225971 4208455 HRE-IK.B2
and below is the main columns of the 'box' table which i need to delete the row which is affected by me deleting it in the items table (this is the part i got stuck)
BOX_ID ORDERNO BOX BOXOF
-------------------------------
1027766 1225971 1 1
1027767 1225971 1 2
1027768 1225971 2 2
Below is the query i tried to find the correct items, and this query either showed no results, or too many that i don't need (as i am trying to sort only by date)
select
items.item, items.ORDERNO, items.LU_ON,
stock.NUMBER, stock.CUSTOMTEXT, stock.LU_ON,
box.orderno, box.LU_ON, box.BOX_ID, box.box
from
BOX, items, STOCK
where
box.boxof not in (select ORDERNO from items)
and items.item = stock.NUMBER
and box.LU_ON >= '2021-11-10'
and items.LU_ON >= '2021-11-10'
and convert(VARCHAR, STOCK.CUSTOMTEXT) = 'breakout'
and convert(VARCHAR, items.ORDERNO) <> ''
So with this too much info shows up, not sure how to isolate what I am looking for, also then i will need to delete what I found, once i know it is correct.
I hope my question was clear enough, it is my first time asking here, and would be happy to clarify/ take pointers.
Thank you!

Related

Merging and transposing in SQL Server

I have two tables:
Table 1
SOP Number is unique per order. For the table above, INVO23935 is one order and INV023937 is another order, etc.
Each unique item in an order has its own line and there could be 1 or more of those items. For INVO23935, there are three item X and one item Y. But other orders could have the same item. For example, order INV023948 has one item X. There are thousands of different items and new ones are constantly added.
The line column is unique to each line item on an order. It is incremented by 16384 for every line item. However, I don’t need line for the final table but I thought it might be useful to create the final table. If it’s not, you can delete it.
This is just a snapshot of the table, which has thousands of rows.
Table 2
Table 2 has one different column from table 1 and that is serial numbers. Only certain items have serial numbers and each serial number is unique even for the same item. For example, there are three item X in INV023935 and three different serial numbers for each X.However, some items won’t have serial numbers so it won’t show up in table 2. For example, Item N1 in INV023937 don’t have any serial numbers; hence, it will only show up in Table 1.
I would like to merge to two tables to look like this:
New Table I want to Make
There are thousands of rows and it’s constantly being updated so I can’t do this manually. I hope I can use SQL to create the table above.
I will really be grateful if you can help me with this!
This would work for SQL Server to always show Table1 values, then show concatenated serial numbers from Table2 if available (or blank otherwise)
SELECT
T1.[Date] AS [Document Date],
T1.[SOP Number],
T1.QTY,
T1.Line AS [Line Item Sequence],
T1.Item AS [Item Number],
COALESCE(STUFF((
SELECT ',' + T2.[Serial Number] AS [text()]
FROM Table2 T2
WHERE T1.[SOP Number] = T2.[SOP Number]
AND T1.[Line] = T2.[Line]
-- Any other join conditions (if item is also required or not)
FOR XML PATH('')
), 1, 1, ''), '') AS [Serial Number]
FROM Table1 T1

SQL Server - Update All Records, Per Group, With Result of SubQuery

If anyone could even just help me phrase this question better I'd appreciate it.
I have a SQL Server table, let's call it cars, which contains entries representing items and information about their owners including car_id, owner_accountNumber, owner_numCars.
We're using a system that sorts 'importantness of owner' based on number of cars owned, and relies on the owner_numCars column to do so. I'd rather not adjust this, if reasonably possible.
Is there a way I can update owner_numCars per owner_accountNumber using a stored procedure? Maybe some other more efficient way I can accomplish every owner_numCars containing the count of entries per owner_accountNumber?
Right now the only way I can think to do this is to (from the c# application):
SELECT owner_accountNumber, COUNT(*)
FROM mytable
GROUP BY owner_accountNumber;
and then foreach row returned by that query
UPDATE mytable
SET owner_numCars = <count result>
WHERE owner_accountNumber = <accountNumber result>
But this seems wildly inefficient compared to having the server handle the logic and updates.
Edit - Thanks for all the help. I know this isn't really a well set up database, but it's what I have to work with. I appreciate everyone's input and advice.
This solution takes into account that you want to keep the owner_numCars column in the CARs table and that the column should always be accurate in real time.
I'm defining table CARS as a table with attributes about cars including it's current owner. The number of cars owned by the current owner is de-normalized into this table. Say I, LAS, own three cars, then there are three entries in table CARS, as such:
car_id owner_accountNumber owner_numCars
1 LAS1 3
2 LAS1 3
3 LAS1 3
For owner_numCars to be used as an importance factor in a live interface, you'd need to update owner_numCars for every car every time LAS1 sells or buys a car or is removed from or added to a row.
Note you need to update CARS for both the old and new owners. If Sam buys car1, both Sam's and LAS' totals need to be updated.
You can use this procedure to update the rows. This SP is very context sensitive. It needs to be called after rows have been deleted or inserted for the deleted or inserted owner. When an owner is updated, it needs to be called for both the old and new owners.
To update real time as accounts change owners:
create procedure update_car_count
#p_acct nvarchar(50) -- use your actual datatype here
AS
update CARS
set owner_numCars = (select count(*) from CARS where owner_accountNumber = #p_acct)
where owner_accountNumber = #p_acct;
GO
To update all account_owners:
create procedure update_car_count_all
AS
update C
set owner_numCars = (select count(*) from CARS where owner_acctNumber = C.owner_acctNumber)
from CARS C
GO
I think what you need is a View. If you don't know, a View is a virtual table that displays/calculates data from a real table that is continously updated as the table data updates. So if you want to see your table with owner_numCars added you could do:
SELECT a.*, b.owner_numCars
from mytable as a
inner join
(SELECT owner_accountNumber, COUNT(*) as owner_numCars
FROM mytable
GROUP BY owner_accountNumber) as b
on a.owner_accountNumber = b.owner_accountNumber
You'd want to remove the owner_numCars column from the real table since you don't need to actually store that data on each row. If you can't remove it you can replace a.* with an explicit list of all the fields except owner_numCars.
You don't want to run SQL to update this value. What if it doesn't run for a long time? What if someone loads a lot of data and then runs the score and finds a guy that has 100 cars counts as a zero b/c the update didn't run. Data should only live in 1 place, updating has it living in 2. You want a view that pulls this value from the tables as it is needed.
CREATE VIEW vOwnersInfo
AS
SELECT o.*,
ISNULL(c.Cnt,0) AS Cnt
FROM OWNERS o
LEFT JOIN
(SELECT OwnerId,
COUNT(1) AS Cnt
FROM Cars
GROUP BY OwnerId) AS c
ON o.OwnerId = c.OwnerId
There are a lot of ways of doing this. Here is one way using COUNT() OVER window function and an updatable Common Table Expression [CTE]. That you won't have to worry about relating data back, ids etc.
;WITH cteCarCounts AS (
SELECT
owner_accountNumber
,owner_numCars
,NewNumberOfCars = COUNT(*) OVER (PARTITION BY owner_accountNumber)
FROM
MyTable
)
UPDATE cteCarCounts
SET owner_numCars = NewNumberOfCars
However, from a design perspective I would raise the question of whether this value (owner_numCars) should be on this table or on what I assume would be the owner table.
Rominus did make a good point of using a view if you want the data to always reflect the current value. You could also use also do it with a table valued function which could be more performant than a view. But if you are simply showing it then you could simply do something like this:
SELECT
owner_accountNumber
,owner_numCars = COUNT(*) OVER (PARTITION BY owner_accountNumber)
FROM
MyTable
By adding a where clause to either the CTE or the SELECT statement you will effectively limit your dataset and the solution should remain fast. E.g.
WHERE owner_accountNumber = #owner_accountNumber

SQL Server FullText Search with Weighted Columns from Previous One Column

In the database on which I am attempting to create a FullText Search I need to construct a table with its column names coming from one column in a previous table. In my current implementation attempt the FullText indexing is completed on the first table Data and the search for the phrase is done there, then the second table with the search results is made.
The schema for the database is
**Players**
Id
PlayerName
Blacklisted
...
**Details**
Id
Name -> FirstName, LastName, Team, Substitute, ...
...
**Data**
Id
DetailId
PlayerId
Content
DetailId in the table Data relates to Id in Details, and PlayerId relates to Id in Players. If there are 1k rows in Players and 20 rows in Details, then there are 20k rows in Data.
WITH RankedPlayers AS
(
SELECT PlayerID, SUM(KT.[RANK]) AS Rnk
FROM Data c
INNER JOIN FREETEXTTABLE(dbo.Data, Content, '"Some phrase like team name and player name"')
AS KT ON c. DataID = KT.[KEY]
GROUP BY c.PlayerID
)
…
Then a table is made by selecting the rows in one column. Similar to a pivot.
…
SELECT rc.Rnk,
c.PlayerID,
PlayerName,
TeamID,
…
(SELECT Content FROM dbo.Data data WHERE DetailID = 1 AND data.PlayerID = c.PlayerID) AS [TeamName],
…
FROM dbo.Players c
JOIN RankedPlayers rc ON c. PlayerID = rc. PlayerID
ORDER BY rc.Rnk DESC
I can return a ranked table with this implementation, the aim however is to be able to produce results from weighted columns, so say the column Playername contributes to the rank more than say TeamName.
I have tried making a schema bound view with a pivot, but then I cannot index it because of the pivot. I have tried making a view of that view, but it seems the metadata is inherited, plus that feels like a clunky method.
I then tried to do it as a straight query using sub queries in the select statement, but cannot due to indexing not liking sub queries.
I then tried to join multiple times, again the index on the view doesn't like self-referencing joins.
How to do this?
I have come across this article http://developmentnow.com/2006/08/07/weighted-columns-in-sql-server-2005-full-text-search/ , and other articles here on weighted columns, however nothing as far as I can find addresses weighting columns when the columns were initially row data.
A simple solution that works really well. Put weight on the rows containing the required IDs in another table, left join that table to the table to which the full text search had been applied, and multiply the rank by the weight. Continue as previously implemented.
In code that comes out as
DECLARE #Weight TABLE
(
DetailID INT,
[Weight] FLOAT
);
INSERT INTO #Weight VALUES
(1, 0.80),
(2, 0.80),
(3, 0.50);
WITH RankedPlayers AS
(
SELECT PlayerID, SUM(KT.[RANK] * ISNULL(cw.[Weight], 0.10)) AS Rnk
FROM Data c
INNER JOIN FREETEXTTABLE(dbo.Data, Content, 'Karl Kognition C404') AS KT ON c.DataID = KT.[KEY]
LEFT JOIN #Weight cw ON c.DetailID = cw.DetailID
GROUP BY c.PlayerID
)
SELECT rc.Rnk,
...
I'm using a temporary table here for evidence of concept. I am considering adding a column Weights to the table Details to avoid an unnecessary table and left join.

Filter Duplicate records by closest data?

Background
I have multiple records in a table that some times have duplicate entries, apart from the data the record was created.
I have to pick between the duplicate records and change a field of the one with the latest date (last one to be created).
Currently I am doing this manually by visually checking the dates.
Question
Is here a way of only bring back one of the duplicates, the one with the closest day to today?
Example
Below is a query that brings back two sets of duplicates for one stationID. There should only be one record per assessment type. The isLive column would be changed to True for the bottom two record as they have the latest Filedate records.
SQL
SELECT StationFileID
,StationID
,AssessmentType
,URL
,FileDate
,isLive
,StationObjectID
FROM StationFiles
WHERE StationID = '1066'
ORDER BY StationID;
Records Returned
You can use the ROW_NUMBER() function to identify the latest rows:
SELECT *
,CASE WHEN N = 1 THEN 'True'
ELSE 'False' END AS isLive
FROM (SELECT StationFileID
,StationID
,AssessmentType
,FileDate
,ROW_NUMBER() OVER (PARTITION BY StationID, AssessmentType ORDER BY FileDate DESC) AS N
FROM StationFiles
WHERE StationID = '1066') AS T

SQL Persisted Computed Column with Subquery

I have three tables
Table 1: Items
ItemID | DaysLastSold
Table2: Listings
ItemID | ListingID
Table3: Sales
ListingID | DateItemClosed
I got this query to work:
SELECT min(DATEDIFF(day, DateItemClosed, getdate())) as DaysLastSold
from Sales
where QtySold > 0
and ListingID in (SELECT ListingID from Listings where ItemID = 8101 )
What I'm trying to do is basically place this query into the DaysLastSold Column in the Items table. So when ever the column is selected it recalculates DaysLastSold using the ItemID in the neighboring column.
If you want to persist that information you could create an indexed view that is made up of your calculated value and an ItemID. Obviously this would not be a column in your original table though. You could then join in on this view when you need the information.
Personally I would probably just do it inline when you need it. If you are concerned about performance, post the execution plan here and we may be able to make some suggestions.

Resources