Update table lookup - tsql

I have a temp table (#WorkTable) which looks like the following:
InstrID CountryName CreditRating
1 UK AA
2 UK Unclassified
3 South Africa A
4 South Africa A
5 South Africa Unclassified
What I want to be able to do is update this table where column CreditRating is 'Unclassified' with its actual credit rating. So in the example above the UK unclassified would become 'AA' and the South African one would become 'A'.
I'm unsure of the coding for this, any help would be greatly appreciated.

The sql script below will update any unclassified to the 'actual' credit ratings. I hope this helps.
CREATE TABLE #WorkTable
(
InstrID INT,
CountryName VARCHAR(50),
CreditRating VARCHAR(20)
)
INSERT INTO #WorkTable VALUES ( 1, 'UK','AA');
INSERT INTO #WorkTable VALUES ( 2, 'UK','Unclassified');
INSERT INTO #WorkTable VALUES ( 3, 'South Africa','A');
INSERT INTO #WorkTable VALUES ( 4, 'South Africa','A');
INSERT INTO #WorkTable VALUES ( 5, 'South Africa','Unclassified');
WITH cteUnclassified
AS
(
SELECT InstrID,
CountryName,
CeditRating
FROM #WorkTable
WHERE CreditRating != 'Unclassified'
)
UPDATE #WorkTable
SET CreditRating = u.CreditRating
FROM #WorkTable wt
INNER JOIN cteUnclassified u
ON wt.CountryName = u.CountryName
WHERE wt.CreditRating = 'Unclassified'
SELECT *
FROM #WorkTable
Result of the query below:
InstrID CountryName CreditRating
1 UK AA
2 UK AA
3 South Africa A
4 South Africa A
5 South Africa A

I think This example would help you.
String sql = "update table_name set CreditRating = (select unique CreditRating from table_name where CountryName = ? and CreditRating != 'classifie...') where CountryName = ? and CreditRating = 'classifie...'";
Here you can pass the countryName as a paramaeter.

You seem new to SQL. What you need to do is "join" the table to the lookup values. You can find a lot of help on basics of SQL in a lot of places, but if you need a quick solution, try the following:
If you havea seperate table with countries and credit ratings, (I have assumed the name of that table is Rating, and the country names and rating match.)
update #WorkTable
Set w.Creditrating = r.CreditRating
from #WorkTable w join Rating r on w.CountryName=r.CountryName
where w.CreditRating='Unclassified'
On the other hand, if no seperate table exists, and you want to update the unclassified values based on the same temp table, then you need to assume that there is some entry with a CreditRating for that country in the table already (which, in the case you posted, there is.) It also needs for there to be only one CreditRating per country. In this case, the following should work:
update #WorkTable
Set w.Creditrating = w2.CreditRating
from #WorkTable w join
(Select distinct CountryName, CreditRating from #WorkTable where CreditRating<>'Unclassified') w2
on w.CountryName=w2.CountryName
where w.CreditRating='Unclassified'
This uses the table itself to find the values, by looking at the table twice - once as the table to update, where the last line restricts what is updated to only the unclassified items, and once as a source table, where only classified ratings are used. Please leave a comment with more details if this doesn't work for you.

Related

I need to use the rows of one column to select another column

I have as selection of Columns called Parameter(1 to 10) and I need the row select to join on the column of another table, depending on the Matrix selected. This needs to be driven by the matrix selected. The rows to columns heading is a 1-1 mapping, but the values are 1 to many. Like below:
Matrix 1 - Name, Parameter1 = First Name, Parameter2 = Last Name
Matrix 2 - Location, Parameter1 = City, Parameter2 = State, Parameter3 = Country
All the data a held in the table client detail table. The base table is over 50 columns long and there are over 100 different parameters, along with 60+ matrix possibilities.
Example - Base Table
First Name
Last Name
City
State
Country
Mary
Smith
Austin
Texas
USA
Steven
Johnson
Toronto
Ontario
Canada
Matrix
Parameter1
Parameter2
Parameter3
1
City
State
Country
2
First Name
Last Name
I need the outputs to be like :
Output A
When Matrix 1 is selected
City
State
Country
Austin
Texas
USA
Toronto
Ontario
Canada
Output B
When Matrix 2 selected
First Name
Last Name
Mary
Smith
Steven
Johnson
I believe you can do what you want using dynamic SQL that builds up the query and the variable conditions into a SQL string and then executes the query using sp_ExecuteSql.
Something like:
DECLARE #ConditionTemplate VARCHAR(MAX) = 'B.<ColumnName> = #Parameter<ParameterId>'
DECLARE #ConditionSeparator VARCHAR(50) = '
AND '
DECLARE #Conditions NVARCHAR(MAX) = (
SELECT STRING_AGG(REPLACE(REPLACE(
#ConditionTemplate
, '<ColumnName>', QUOTENAME(P.ColumnName))
, '<ParameterId>', CAST(P.ParameterId AS VARCHAR))
, #ConditionSeparator)
FROM Matrix M
CROSS APPLY (
VALUES
(1, M.Parameter1),
(2, M.Parameter2),
(3, M.Parameter3)
) P(ParameterId, ColumnName)
WHERE M.MatrixId = #MatrixId
AND P.ColumnName > ''
)
DECLARE #Sql NVARCHAR(MAX) = '
SELECT *
FROM BaseTable B
WHERE '
+ #Conditions
DECLARE #Params NVARCHAR(MAX) = N'#Parameter1 VARCHAR(100), #Parameter2 VARCHAR(100), #Parameter3 VARCHAR(100)'
PRINT #Sql
EXEC sp_ExecuteSql #Sql, #Params, #Parameter1, #Parameter2, #Parameter3
Most of the work is done in the #Conditions calculation. That calculation selects the proper matrix row, flatens out the data by mapping each parameter column into a numbered row, formats each into a "column = #Parameter" equality comparison, and then uses STRING_AGG() to join the conditions together. That list of conditions is then combined with the rest of the query and executed. Because the executed dynamic SQL cannot access the parameters from the calling sql, the parameters must be explicitly passed in via the sp_ExecuteSql call.
Given the following parameters:
DECLARE #MatrixId INT = 2
DECLARE #Parameter1 VARCHAR(100) = 'Steven'
DECLARE #Parameter2 VARCHAR(100) = 'Johnson'
DECLARE #Parameter3 VARCHAR(100) = NULL
The generated SQL would be as follows:
SELECT *
FROM BaseTable B
WHERE B.[First Name] = #Parameter1
AND B.[Last Name] = #Parameter2
Which will yield the following result:
First Name
Last Name
City
State
Country
Steven
Johnson
Toronto
Ontario
Canada
See this db<>fiddle.
I tried to repro this. Below is the SQL script.
Input Tables
1. base_table
First Name
Last Name
City
State
Country
Mary
Smith
Austin
Texas
USA
Steven
Johnson
Toronto
Ontario
Canada
2. Matrix table
Matrix
Parameter1
Parameter2
Parameter3
1
City
State
Country
2
[First Name]
[Last Name]
null
SQL script:
DECLARE #StgTable TABLE ([ColName] varchar(256), Done bit default(0));
Insert into #StgTable( [ColName] ) select concat_ws(',',parameter1,parameter2,parameter3) from matrix_table
DECLARE #SQLstmnt nvarchar(max), #colnames varchar(200);
WHILE EXISTS (SELECT 1 FROM #StgTable WHERE Done = 0) BEGIN
SELECT TOP 1 #colnames = [ColName] FROM #StgTable WHERE Done = 0;
SET #SQLstmnt = 'SELECT '+ #colnames +' FROM base_table ;';
EXECUTE sp_executesql #SQLstmnt;
UPDATE #StgTable SET Done = 1 WHERE [ColName] = #colnames;
END;
See this db<>fiddle
Output:
City
State
Country
Austin
Texas
USA
Toronto
Ontario
Canada
First Name
Last Name
Mary
Smith
Steven
Johnson

Unpivot Columns with Most Recent Record

Student Records are updated for subject and update date. Student can be enrolled in one or multiple subjects. I would like to get each student record with most subject update date and status.
CREATE TABLE Student
(
StudentID int,
FirstName varchar(100),
LastName varchar(100),
FullAddress varchar(100),
CityState varchar(100),
MathStatus varchar(100),
MUpdateDate datetime2,
ScienceStatus varchar(100),
SUpdateDate datetime2,
EnglishStatus varchar(100),
EUpdateDate datetime2
);
Desired query output, I am using CTE method but trying to find alternative and better way.
SELECT StudentID, FirstName, LastName, FullAddress, CityState, [SubjectStatus], UpdateDate
FROM Student
;WITH orginal AS
(SELECT * FROM Student)
,Math as
(
SELECT DISTINCT StudentID, FirstName, LastName, FullAddress, CityState,
ROW_NUMBER OVER (PARTITION BY StudentID, MathStatus ORDER BY MUpdateDate DESC) as rn
, _o.MathStatus as SubjectStatus, _o.MupdateDate as UpdateDate
FROM original as o
left join orignal as _o on o.StudentID = _o.StudentID
where _o.MathStatus is not null and _o.MUpdateDate is not null
)
,Science AS
(
...--Same as Math
)
,English AS
(
...--Same As Math
)
SELECT * FROM Math WHERE rn = 1
UNION
SELECT * FROM Science WHERE rn = 1
UNION
SELECT * FROM English WHERE rn = 1
First: storing data in a denormalized form is not recommended. Some data model redesign might be in order. There are multiple resources about data normalization available on the web, like this one.
Now then, I made some guesses about how your source table is populated based on the query you wrote. I generated some sample data that could show how the source data is created. Besides that I also reduced the number of columns to reduce my typing efforts. The general approach should still be valid.
Sample data
create table Student
(
StudentId int,
StudentName varchar(15),
MathStat varchar(5),
MathDate date,
ScienceStat varchar(5),
ScienceDate date
);
insert into Student (StudentID, StudentName, MathStat, MathDate, ScienceStat, ScienceDate) values
(1, 'John Smith', 'A', '2020-01-01', 'B', '2020-05-01'),
(1, 'John Smith', 'A', '2020-01-01', 'B+', '2020-06-01'), -- B for Science was updated to B+ month later
(2, 'Peter Parker', 'F', '2020-01-01', 'A', '2020-05-01'),
(2, 'Peter Parker', 'A+', '2020-03-01', 'A', '2020-05-01'), -- Spider-Man would never fail Math, fixed...
(3, 'Tom Holland', null, null, 'A', '2020-05-01'),
(3, 'Tom Holland', 'A-', '2020-07-01', 'A', '2020-05-01'); -- Tom was sick for Math, but got a second chance
Solution
Your question title already contains the word unpivot. That word actually exists in T-SQL as a keyword. You can learn about the unpivot keyword in the documentation. Your own solution already contains common table expression, these constructions should look familiar.
Steps:
cte_unpivot = unpivot all rows, create a Subject column and place the corresponding values (SubjectStat, Date) next to it with a case expression.
cte_recent = number the rows to find the most recent row per student and subject.
Select only those most recent rows.
This gives:
with cte_unpivot as
(
select up.StudentId,
up.StudentName,
case up.[Subject]
when 'MathStat' then 'Math'
when 'ScienceStat' then 'Science'
end as [Subject],
up.SubjectStat,
case up.[Subject]
when 'MathStat' then up.MathDate
when 'ScienceStat' then up.ScienceDate
end as [Date]
from Student s
unpivot ([SubjectStat] for [Subject] in ([MathStat], [ScienceStat])) up
),
cte_recent as
(
select cu.StudentId, cu.StudentName, cu.[Subject], cu.SubjectStat, cu.[Date],
row_number() over (partition by cu.StudentId, cu.[Subject] order by cu.[Date] desc) as [RowNum]
from cte_unpivot cu
)
select cr.StudentId, cr.StudentName, cr.[Subject], cr.SubjectStat, cr.[Date]
from cte_recent cr
where cr.RowNum = 1;
Result
StudentId StudentName Subject SubjectStat Date
----------- --------------- ------- ----------- ----------
1 John Smith Math A 2020-01-01
1 John Smith Science B+ 2020-06-01
2 Peter Parker Math A+ 2020-03-01
2 Peter Parker Science A 2020-05-01
3 Tom Holland Math A- 2020-07-01
3 Tom Holland Science A 2020-05-01

Finding rows that have the same value with Postgres

Not sure if the title is descriptive enough (sorry about the bad english), but my problem is that I need to find the rows that have the same value in all rows.
Possibly easier to understand what I actually mean with an example:
I have an table that's called Catalogue with the attributes motortype, motorpart and partnumber where motortype and motorpart are VARCHAR and partnumber are int.
Some partnumbers are used in all the motortypes, and it's those partnumbers that I am interested in finding.
I'm having a hard time seeing how to solve this, so any help would be greatly appreciated!
EDIT:
Sorry if the question was lackful or bad. I have updated with the table schema, some sample data and my desired results under:
CREATE TABLE Catalogue (
motortype VARCHAR,
motorpart VARCHAR,
partnumber int,
PRIMARY KEY (partnumber)
);
Sample data:
INSERT INTO Catalogue VALUES ('M1', 'Brakes', 1);
INSERT INTO Catalogue VALUES ('M2', 'Brakes', 1);
INSERT INTO Catalogue VALUES ('M3', 'Brakes', 1);
INSERT INTO Catalogue VALUES ('M1', 'Gasket', 2);
INSERT INTO Catalogue VALUES ('M2', 'Gasket', 2);
INSERT INTO Catalogue VALUES ('M3', 'Pedal', 3);
Desired result:
| motorpart | partnumber |
|-----------|------------|
| Brakes | 1 |
Hope this was better formulated!
If there are 3 different types you can use next query:
select motorpart
from Catalogue
group by motorpart
having count(distinct motortype) >= 3;
If you don't know the number:
select motorpart
from Catalogue
group by motorpart
having count(distinct motortype) = (select distinct motortype
from Catalogue);
Rextester here

Updating a column in Postgres only returning one value incorrectly (Postgres 9.5.1)

Suppose I have a table created by the following:
create table test.sales
(
customer text,
purchased text,
date_purchased date,
rownumber integer,
primary key (customer, date_purchased)
)
;
insert into test.sales
values
('kevin', 'books', '2017-01-01'::date, null),
('kevin', 'movies', '2017-01-02'::date, null),
('paul', 'books', '2017-01-05'::date, null),
('paul', 'movies', '2017-01-07'::date, null)
At this point, rownumber is always NULL and I want to set the value of rownumber with row_number() over (partition by customer order by date_purchased) as rownumber. The way I am going about this is the following:
update test.sales as a
set (rownumber) =
(
select
row_number() over (partition by customer order by date_purchased) as rownumber
from test.sales as b
-- These fields correspond to the primary keys of the table.
where a.customer = b.customer
and a.date_purchased = b.date_purchased
)
But for some reason this returned:
customer purchased date_purchased rownumber
kevin books 1/1/17 1
kevin movies 1/2/17 1
paul books 1/5/17 1
paul movies 1/7/17 1
I'm expecting this:
customer purchased date_purchased rownumber
kevin books 1/1/17 1
kevin movies 1/2/17 2
paul books 1/5/17 1
paul movies 1/7/17 2
Notice that in the actual results, rownumber is always 1. Why is this?
You are ussing a scalar subquery. Use a correlated subquery instead:
UPDATE test.sales dst
SET rownumber = src.rownumber
FROM ( SELECT customer,date_purchased
, row_number() over (partition by customer order by date_purchased) as rownumber
from test.sales
) src
WHERE dst.customer = src.customer
AND dst.date_purchased = src.date_purchased
;

ORACLE:- 'SELECT ORDER BY ASC' but 'USA' always first

I have to write a drop down query for countries.
But USA should always be first.
The rest of the countries are in alphabetical order
I tried the following query
SELECT
countries_id
,countries_name
FROM get_countries
WHERE
countries_id = 138
UNION
SELECT
countries_id
,countries_name
FROM get_countries
WHERE
countries_id != 138
ORDER BY 2 ASC
Something like this maybe:
ORDER BY
CASE
WHEN upper(country_name) = 'USA' then '0'
ELSE lower(country_name)
END
Here's a complete example
create TABLE countries (country_name VARCHAR2(50));
INSERT INTO countries VALUES ('USA');
INSERT INTO countries VALUES ('India');
INSERT INTO countries VALUES ('Russia');
INSERT INTO countries VALUES ('China');
COMMIT;
SELECT country_name
FROM countries
ORDER BY
CASE
WHEN upper(country_name) = 'USA' then '0'
ELSE lower(country_name)
END
Returns:
USA
China
India
Russia
It's been a while since I worked with oracle, but you can try ORDER BY countries_name = 'USA', countries_name ASC.
Correction
Sorry that didn't work. I had "countries_name" mis-typed as "country_name", so it may work now.
You could also use ORDER BY decode(countries_name, 'USA', 0, 1), countries_name ASC.