Updating column based on three tables - sql-server

I know it's very unprofessional, but it's our business system so I can't change it.
I have three tables: t_posList, t_url, t_type. The table t_posList has a column named URL which is also stored in the table t_url (the ID of the table t_url is not saved in t_posList so I have to find it like posList.Url = t_url.Url).
The column t_posList.status of every data row should be updated to 'non-customer' (it will be a status id but lets keep it simple) if: the ID of t_url can NOT be found in t_type.url_id.
So the query has like two steps: first I have to get all of the data rows where t_posList.Url = t_url.Url. After this I have to check which ID's of the found t_url rows can NOT be found in t_type.url_id.
I really hope you know what I mean. Because our system is very unprofessional and my SQL knowledge is not that good I'm not able to make this query.
EDIT: I tried this:
UPDATE t_poslist SET status = (
SELECT 'non-customer'
FROM t_url, t_type
WHERE url in
(select url from t_url
LEFT JOIN t_type ON t_url.ID = t_type.url_id
WHERE t_type.url_id is null)
)

What about this?
UPDATE p
SET status = 'non-customer'
FROM t_poslist p
INNER JOIN t_url u ON u.url = p.url
WHERE NOT EXISTS
(
SELECT * FROM t_type t WHERE t.url_id = u.ID
)

Related

SQL Server : update join statement of two tables with multiple columns from existing table to match

I need to update an existing table (First) with a new column (Ticket_No_Calc). Basically, there are two columns (Doc_Header and Doc_No) that I have to lookup/refer to in the first table against the second table (Doc_No) column.
The desired column Ticket_No_Calc result is retrieved from the column Ticket_No.
Below is the screenshot of sample data of the first table
and this is the screenshot of sample data of the second table
Currently, in Excel, I use the following formula to get the desired column in the existing table
=IFERROR(VLOOKUP(FIRST!B2,'SECOND'!A:B,1,0),(VLOOKUP(FIRST!C2,'SECOND'!A:B,1,0)))
I am not sure how to do this in SQL. I know that I can get this done in SQL with the following formula if I only have to refer to one column Doc_Header:
UPDATE A
SET A.Ticket_No_Calc = B.Ticket_No
FROM First AS A
LEFT JOIN Second AS B ON A.Doc_Header = B.Doc_NO ;
But I need to refer to two columns in the existing table. I have been researching for sometimes to get the correct command but I can't seem to be able to crack it.
Following statement may help
UPDATE A
SET A.Ticket_No_Calc = B.Ticket_No
FROM First AS A
INNER JOIN Second AS B ON (A.Doc_Header = B.Doc_NO) OR (A.Doc_NO = B.Doc_NO) ;
As per the sample data provided this is query seems incorrect. But as per the comments mentioned in your question it is better to perform this in 2 steps.
UPDATE A
SET A.Ticket_No_Calc = B.Ticket_No
FROM First AS A
LEFT JOIN Second AS B ON A.Doc_Header = B.Doc_NO ;
UPDATE A
SET A.Ticket_No_Calc = B.Ticket_No
FROM First AS A
LEFT JOIN Second AS B ON A.DOC_NO=B.DOC_NO
WHERE A.Doc_Header <> B.Doc_NO
Try this:
UPDATE A
SET A.Ticket_No_Calc = B.Ticket_No
FROM First AS A ,Second AS B where A.Doc_Header = B.Doc_NO or A.Doc_NO = B.Doc_NO

I have two tables and wanted to make a left join and get the latest data using date from both the tables. It doesn't pull all data from left table

I have two tables and wanted to make a left join and get the latest data using date from both the tables. It doesn't pull all data from left table
SELECT Firsttable.Username, Secondtable.city
FROM Firsttable
LEFT JOIN Secondtable ON (Firsttable.Username = Secondtable.Account_Name)
WHERE (
Firsttable.Questions <> 5
AND Firsttable.CreateDate = '2018-02-06 09:41:38.000'
AND Secondtable.CreateDate = '2018-02-06 09:07:47.000'
)
OR (
Firsttable.Questions <> 5
AND Firsttable.CreateDate = '2018-02-06 09:41:38.000'
AND Secondtable.CreateDate IS NULL
)
Since you didn't provide us with any data, you are going to have to do your own testing.
First, break your query down into smaller parts and see if the data you want actually exists.
SELECT Firsttable.Username
FROM Firsttable
LEFT JOIN Secondtable ON
(Firsttable.Username = Secondtable.Account_Name
AND Firsttable.Questions <> 5
AND Firsttable.CreateDate = '2018-02-06 09:41:38.000'
)
This will show if you have data in the first table that is valid. If you don't get anything, you may need to relax your Date comparison by eliminating Time. But only you know how restricting your data is. It also may mean your Secondtable.Account_Name may not match up to your Firsttable.Username like you thought. Play around with this query until you get the good data you were expecting.
Once you know you have good data in your first table, then add on selection criteria for your second table:
SELECT Firsttable.Username, Secondtable.city
FROM Firsttable
LEFT JOIN Secondtable ON
(Firsttable.Username = Secondtable.Account_Name
AND Firsttable.Questions <> 5
AND Firsttable.CreateDate = '2018-02-06 09:41:38.000'
)
WHERE (
Secondtable.CreateDate = '2018-02-06 09:07:47.000'
OR Secondtable.CreateDate IS NULL
)
If this drops off all of your data, then you know something doesn't match up with your Secondtable.CreateDate. It could be your use of Time is too constricting. Just match on date only and see if that helps. But if you know Time is important, go back to the first query above and print out all the different Secondtable.CreateDate to see if your date is part of the result set. Being off by one second could cause your data to not match.
Play around with both of these queries until you find the combination that brings your data out.
If you still have trouble, you'll have to post some example data for each table so we can see how to help you better.

SQL Server - Dynamic Pivot using existing table

I have the following query
SELECT CONCAT(RIGHT('00' + CONVERT(VARCHAR(2),Procedures.SeriesNum),2),'-',RIGHT('0000' + CONVERT(VARCHAR(4),Procedures.ProcNum),4)) AS 'Procedure',
Procedures.Description,
Procedures.CurrentRev,
Procedures.DayToDayLevel,
Procedures.MaxLevel,
Users.Username,
UsersProcedures.RevTrained,
UsersProcedures.LevelTrained
FROM Procedures
CROSS JOIN Users
LEFT JOIN UsersProcedures ON UsersProcedures.Username = Users.Username AND Procedures.SeriesNum = UsersProcedures.SeriesNum AND Procedures.ProcNum = UsersProcedures.ProcNum
Which returns the following results:
However I would like to use a Pivot so that the info for each user (Specifically The Level they were trained at is shown as a value in a column (With the column title being that of the username).
I would like to ignore the CurrentRev, and instead have 1 row for each Revision for each procedure (if no users have been trained to the current revision - whether users have been trained to any previous revisions on that procedure or not, that should be there as an empty row), like below:
I presume I need to use a pivot, although i've never attempted it before, plus the examples i've seen on the web seem to use a static list for the pivoted columns, whereas I want to use all records in the Users table.
I should point out, when it comes to displaying the results in a datagrid, it'll be coloured for clarity.
How would I go about attempting this?
UPDATE: I've got as far as this query
SELECT *
FROM (SELECT CONCAT(RIGHT('00' + CONVERT(VARCHAR(2),Procedures.SeriesNum),2),'-',RIGHT('0000' + CONVERT(VARCHAR(4),Procedures.ProcNum),4)) AS 'Procedure',
Procedures.Description,
Procedures.CurrentRev,
UsersProcedures.RevTrained,
Users.Username,
UsersProcedures.LevelTrained FROM Procedures CROSS JOIN Users LEFT JOIN UsersProcedures ON UsersProcedures.Username = Users.Username AND Procedures.SeriesNum = UsersProcedures.SeriesNum AND Procedures.ProcNum = UsersProcedures.ProcNum) AS Procs
PIVOT
(
MAX(LevelTrained)
FOR Procs.Username
IN(User1,User2,User3)
) AS PivotTable
Which gives me this:
However i'd still like to group the results further similar to my 2nd screenshot. i.e. if at least 1 user has already been trained on a particular revision, then don't show another empty row for that revision as well.
Also if RevTrained is NULL, then show CurrentRev in its place.
OK, here's the finished solution, using a dynamic query to get all the users, and an IF statement to solve the above problems:
If at least 1 user has already been trained on a particular revision, then don't show another empty row for that revision as well.
Also if RevTrained is NULL, then show CurrentRev in its place.
DECLARE #users AS VARCHAR(MAX),#query AS VARCHAR(MAX)
SELECT #users = STUFF((SELECT ',' + CONCAT('[',Username,']') FROM Users FOR XML PATH(''),TYPE).value('.', 'VARCHAR(MAX)'),1,1,'')
SET #query = 'SELECT *
FROM (SELECT CONCAT(RIGHT(''00'' + CONVERT(VARCHAR(2),Procedures.SeriesNum),2),''-'',RIGHT(''0000'' + CONVERT(VARCHAR(4),Procedures.ProcNum),4)) AS ''Procedure Number'',
CASE WHEN UsersProcedures.RevTrained IS NULL THEN Procedures.CurrentRev ELSE UsersProcedures.RevTrained END AS ''Revision'',
Procedures.Description,
Procedures.DayToDayLevel AS ''Maxmimum Training Level'',
Procedures.DayToDayLevel AS ''Training level for Day to Day Usage'',
Users.Username,
UsersProcedures.LevelTrained FROM Procedures CROSS JOIN Users LEFT JOIN UsersProcedures ON UsersProcedures.Username = Users.Username AND Procedures.SeriesNum = UsersProcedures.SeriesNum AND Procedures.ProcNum = UsersProcedures.ProcNum) AS Procs
PIVOT(
MAX(LevelTrained)
FOR Procs.Username
IN('+#users+')) AS PivotTable'
execute(#query);

Cursor variable not updated

I'm not understanding why the variable, #NextURLId, in this cursor is not being updated. Here is the code
DECLARE #NextURLId INT = 1
DECLARE #varContact_Id INT
DECLARE GetURL_Cursor CURSOR FOR
SELECT DISTINCT(contact_id)
FROM obp.Contacts
OPEN GetURL_Cursor
FETCH NEXT FROM GetURL_Cursor INTO #varContact_id
WHILE ##FETCH_STATUS = 0
BEGIN
-- Available URLs have the used value as NULL. Used has value of 1.
SET #NextURLId = (SELECT MIN(id) FROM obp.URL WHERE used IS NULL)
UPDATE obp.Contacts SET URL = (
SELECT url from obp.URL WHERE id = #NextURLId)
UPDATE obp.URL SET
used = 1,
contact_id = #varContact_Id,
date = GETDATE()
WHERE id = #NextURLId
FETCH NEXT FROM GetURL_Cursor INTO #varContact_id
END;
CLOSE GetURL_Cursor
DEALLOCATE GetURL_Cursor
The code is supposed to retrieve a unique URL from a table (obp.URL), enter that URL in the Contacts table and then update the URL to indicated that the URL has been used. It seems to me that after the URL table is updated with 'used = 1' then the next iteration of the code should get a new URLId when I query for it.
However, when I run this code I get the same URL every time. No doubt I am missing something obvious but need some help to point it out.
As a side, if there is a set based solution for this, I'd be happy to hear it.
TIA
this
UPDATE obp.Contacts SET URL = (
SELECT url from obp.URL WHERE id = #NextURLId)
updates every row with the same. Add a proper WHERE clause like
WHERE contact_id=#varContact_id
About the requirement for this: I understand that you want to associate a Contact with a URL and that there is no logical rule for which with what. At first sight I would consider a match table the right way to do this. It feels better to me to put such associations into a seperate table, even if there is a strong belief in a 1:1-relationship between the two objects associated. obp.URL and obp.Contacts are dimensional tables (I assume/hope). Keeping the association in one different table requires one action if changes occur. In your model a change must be reflected in both those tables.
Here is an idea for such a table:
create table Contact_URL_match
(ID int identity (1,1)
,URL_id int not null unique
,contact_id int not null unique
,created datetime)
the unique constraints disallow insertion of the same URL or the same Contact_id twice. On each insert/update prior content is being checked for duplicates and if found the action is denied, thus uniqueness protected.
For manifesting new matches in a first large initial action try this (haven't tested, just an idea)
INSERT INTO
Contact_URL_match
(URL_id
,contact_id
,created)
SELECT
urls.id
,contacts.contact_id
,getdate()
FROM
(SELECT
DISTINCT(contact_id)
,ROW_NUMBER() over (ORDER BY contact_id asc) rn
FROM
obp.Contacts) contacts
INNER JOIN
(SELECT
id
,ROW_NUMBER() over (ORDER BY id) rn
FROM
obp.URL) urls
ON
contacts.rn=urls.rn
Within the subqueries this creates a row number in both the source tables based on the ORDER BY clauses. It then joins the resultsets of the subqueries by that rownumber which is an act of deliberate randomness. I hope you want that. The result of that join is inserted into the match table.
If later you want to manifest a single new association you could add WHERE clauses to the subqueries that specify what URL you want matched with what Contact. Before picking a URL or Contact check the match table with NOT EXISTS to make sure it is not used in there.
EDIT : syntax errors cleaned

Will this UPDATE accomplish what I intend it to?

This is probably a very simple question for you SQL folks out there.
I have a temp table (TMP_VALIDATION_DATA) in which I've stored the old and new values of some fields I wish to update in a production table (PROVIDER_SERVICE), plus the uuids of the PROVIDER_SERVICE records that need to be updated.
What I want to accomplish is this, in pseudo-code:
For every prov_svc_uuid uuid in TMP_VALIDATION_DATA table
Set PROVIDER_SERVICE_RATE.END_DATE = NewPvSvcEndDate
Where [uuid in temp table] = [uuid in PROVIDER_SERVICE table]
end for
Is this Update statement going to accomplish what I need?
update PROVIDER_SERVICE
set END_DATE = (
select NewPvSvcEndDate
from TMP_VALIDATION_DATA T
where T.PROVIDER_SERVICE_UUID = PROVIDER_SERVICE.PROVIDER_SERVICE_UUID
)
If my UPDATE is incorrect, will you please provide the correction? Thanks.
Your query will update all records and you might get an error if you have more than one record in your subquery. I would also change your syntax to a JOIN similar to below.
update P
set END_DATE = T.NewPvSvcEndDate
FROM PROVIDER_SERVICE P
JOIN TMP_VALIDATION_DATA T
ON P.PROVIDER_SERVICE_UUID = T.PROVIDER_SERVICE_UUID
If you don't want to UPDATE all records, then add a WHERE clause.
My suggestion is if you don't know how many records would be included in the UPDATE, write your query as a SELECT first, then change it to an UPDATE. So for this one:
SELECT P.END_DATE, T.NewPvSvcEndDate
FROM PROVIDER_SERVICE P
JOIN TMP_VALIDATION_DATA T
ON P.PROVIDER_SERVICE_UUID = T.PROVIDER_SERVICE_UUID
This will either update all records, or error out (not sure what happens when you try to update a column with multiple values like that).

Resources