SQL Subquery Column Where Name Contains a Value - sql

I am trying to figure out how to code a certain subquery. I keep getting the "Subquery returned more than 1 value" error. I need to query the description of a parent order instead of the child.
Example:
Parent Order: 100-01
Child Orders: 100-02, 100-03
Each order has a description. But in one column I need the description of the parent:
WHERE [ORDER] LIKE '%-01'
Current code is below:
SELECT NAME as [Job],
[Master Job Desc] = (SELECT TOP 1 [DESCR] FROM TABLE WHERE [NAME] LIKE '%-01'),
LEFT(or_order.descr,30) as [Description],
FROM TABLE
ORDER BY OR_ORDER.DELIVERY asc, or_op.STARTSEC asc
Any advice? Thanks
EDIT: I forgot, I've already tried adding TOP 1 to the subquery, but it just returns the same value for the entire column. I do not want that, I want to find the parent job order.
EDIT2: Here is the separate SQL for what I'm looking for.
SELECT [DESCR] FROM [Or_Order] WHERE [NAME] LIKE '%-01'
The above statements returns descriptions where the name contains the parent job number (-01). I need that in a single column.

You can use charindex() to get the parent part of the order (up to the hyphen) and concatenate that with '01'. With that, you can join to the same table to get the parent order description.
select
Job = c.Name
, [Master Job Desc] = p.Descr
, [Description] = left(c.descr,30)
from or_order as c
left join or_order as p
on left(c.[order],charindex('-',c.[Order]))+'01' = p.[Order]
order by delivery asc, or_op.startsec asc
rextester: http://rextester.com/GSN90790
create table or_order (
[order] varchar(32)
, Name varchar(32)
, Descr varchar(32)
);
insert into or_order values
('100-01','Name 1','Descr 1')
, ('100-02','Name 2','Descr 2')
, ('100-03','Name 3','Descr 3');
select
Job = c.Name
, [Master Job Desc] = p.Descr
, [Description] = left(c.descr,30)
from or_order as c
left join or_order as p
on left(c.[order],charindex('-',c.[Order]))+'01' = p.[Order]
results:
+--------+-----------------+-------------+
| Job | Master Job Desc | Description |
+--------+-----------------+-------------+
| Name 1 | Descr 1 | Descr 1 |
| Name 2 | Descr 1 | Descr 2 |
| Name 3 | Descr 1 | Descr 3 |
+--------+-----------------+-------------+

Related

How to show only the latest record in SQL

I have this issue where I want to show only the latest record (Col 1). I deleted the date column thinking that it might not work if it has different values. but if that's the case, then the record itself has a different name (Col 1) because it has a different date in the name of it.
Is it possible to fetch one record in this case?
The code:
SELECT distinct p.ID,
max(at.Date) as date,
at.[RAPID3 Name] as COL1,
at.[DLQI Name] AS COL2,
at.[HAQ-DI Name] AS COL3,
phy.name as phyi,
at.State_ID
FROM dbo.[Assessment Tool] as at
Inner join dbo.patient as p on p.[ID] = at.[Owner (Patient)_Patient_ID]
Inner join dbo.[Physician] as phy on phy.ID = p.Physician_ID
where (at.State_ID in (162, 165,168) and p.ID = 5580)
group by
at.[RAPID3 Name],
at.[DLQI Name],
at.[HAQ-DI Name],
p.ID, phy.name,
at.State_ID
SS:
In this SS I want to show only the latest record (COL 1) of this ID "5580". Means the first row for this ID.
Thank you
The Most Accurate way to handle this.
Extract The Date.
Than use Top and Order.
create table #Temp(
ID int,
Col1 Varchar(50) null,
Col2 Varchar(50) null,
Col3 Varchar(50) null,
Phyi Varchar(50) null,
State_ID int)
Insert Into #Temp values(5580,'[9/29/2021]-[9.0]High Severity',null,null,'Eman Elshorpagy',168)
Insert Into #Temp values(5580,'[10/3/2021]-[9.3]High Severity',null,null,'Eman Elshorpagy',168)
select top 1 * from #Temp as t
order by cast((Select REPLACE((SELECT REPLACE((SELECT top 1 Value FROM STRING_SPLIT(t.Col1,'-')),'[','')),']','')) as date) desc
This is close to ANSI standard, and it also caters for the newest row per id.
The principle is to use ROW_NUMBER() using a descending order on the date/timestamp (using a DATE type instead of a DATETIME and avoiding the keyword DATE for a column name) in one query, then to select from that query using the result of row number for the filter.
-- your input, but 2 id-s to show how it works with many ..
indata(id,dt,col1,phyi,state_id) AS (
SELECT 5580,DATE '2021-10-03','[10/3/2021] - [9,3] High Severity','Eman Elshorpagy',168
UNION ALL SELECT 5580,DATE '2021-09-29','[9/29/2021] - [9,0] High Severity','Eman Elshorpagy',168
UNION ALL SELECT 5581,DATE '2021-10-03','[10/3/2021] - [9,3] High Severity','Eman Elshorpagy',168
UNION ALL SELECT 5581,DATE '2021-09-29','[9/29/2021] - [9,0] High Severity','Eman Elshorpagy',168
)
-- real query starts here, replace following comman with "WITH" ...
,
with_rank AS (
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY id ORDER BY dt DESC) AS rank_id
FROM indata
)
SELECT
id
, dt
, col1
, phyi
, state_id
FROM with_rank
WHERE rank_id=1
;
id | dt | col1 | phyi | state_id
------+------------+-----------------------------------+-----------------+----------
5580 | 2021-10-03 | [10/3/2021] - [9,3] High Severity | Eman Elshorpagy | 168
5581 | 2021-10-03 | [10/3/2021] - [9,3] High Severity | Eman Elshorpagy | 168

How can join table with IN() in ON couse?

I have two table
User
id | name | category
1 | test | [2,4]
Category
id | name
1 | first
2 | second
3 | third
4 | fourth
now i need to join this both table and get data like:
name | category
test | second, fourth
i tried like:
select u.name as name, c.name as category
from user
INNER JOIN category on(c.id in (u.category))
but it's not working.
As others have suggested, if you have any control whatsoever over the design of this database, don't store multiple values in user.category, but instead have a bridging table between the two which maps one or more category values to each user record.
However, if you are not in a position to be able to redesign the database, here's a way to get the result you're looking for. First, let's create some test data:
create table [user]
(
id int,
[name] varchar(50),
category varchar(50) -- I'm assuming this is a string type
)
create table category
(
id int,
[name] varchar(50)
)
insert into [user] values
(1,'test','[2,4]'),
(2,'another test','[1,2,4]'),
(3,'more test','[1,3,2,4]')
insert into category values
(1,'first'),
(2,'second'),
(3,'third'),
(4,'fourth');
Then you can use a CTE with split_string to pull apart the individual category values, join them to their names, then recombine them into a single comma-separated value with for xml:
with r as
(
select
u.[name] as username,
cat.id,
cat.[name] as categoryname
from [user] u
outer apply
(
select value from string_split(substring(u.category,2,len(u.category)-2),',')
) c
left join category cat on c.value = cat.id
)
select
r.username,
stuff(
(select ',' + categoryname
from r r2
where r.username = r2.username
order by r2.id
for xml path ('')), 1, 1, '') as categories
from r
group by r.username
which gives the desired output:
/-----------------------------------------\
| username | categories |
|-------------|---------------------------|
|another test | first,second,fourth |
|more test | first,second,third,fourth |
|test | second,fourth |
\-----------------------------------------/
I'm making a couple of assumptions here:
You're using MS SQL Server
The category values always begin with [, end with ] and contain nothing but a comma-delimited string containing value category ids

SQL Server Select with two IN clause in conditions

I'm stuck in writing a query. It's a simple query, but I have some doubts. So I want some new approaches on the task
DECLARE #ordersToSet TABLE
(
[Id] [BIGINT] NOT NULL,
[AttributeId] [BIGINT] NOT NULL
)
SELECT *
FROM dbo.Order_Attributes
WHERE OrderAttributeID IN (SELECT AttributeId
FROM #ordersToSet)
AND OrderID IN (SELECT Id
FROM #ordersToSet)
What I want: the query should return only those Order_Attributes where both OrderId and AttributeId match the respective values in temp table #ordersToSet.
Why I think the above code could lead to unexpected results:
#ordersToSet table has the following rows
Id | AttributeId
-------+------------
486455 | 12
OrderAttributes table has the following:
OrderID | OrderAttributeID
--------+-----------------
486455 | 11
635201 | 12
In this case it should NOT select any row, however I doubt it selects A ROW with values OrderId = 486455 and AttributeId = 12
The query should return only those Order_Attributes where both OrderId and AttributeId match the respective values in temp table #ordersToSet.
You seem to be looking for a simple JOIN:
SELECT a.*
FROM dbo.Order_Attributes a
INNER JOIN #ordersToSet s
ON a.OrderAttributeID = s.AttributeId AND a.OrderID = s.Id

Recursively get nested URLs from database

I have a Database table structured with nested URLs, using ParentID and ID to tell which piece of an URL belongs where.
Table structure looks like this:
+-----+----------+------------+-------------+
| ID | ParentID | Name | Url |
+-----+----------+------------+-------------+
| 1 | 0 | Categories | categories |
| 34 | 1 | Movies | movies |
| 281 | 34 | Star Wars | star-wars |
| 33 | 1 | Books | a-good-book |
+-----+----------+------------+-------------+
What I want to do is that I want to be able to recursively go through all of the fields, and according to the ParentID, save all the possible url combinations.
So, from the table above, I'd like to get the following output:
mysite.com/categories
mysite.com/categories/movies
mysite.com/categories/movies/star-wars
mysite.com/categories/books
mysite.com/categories/books/a-good-book
I've started writing a CTE, looking like this:
WITH CategoriesCTE AS
(
SELECT
Name,
Url,
ParentID,
ID
FROM myDB
WHERE ParentID = 1
UNION ALL
SELECT
a.Name,
a.Url,
a.ParentID,
a.ID
FROM myDB.a
INNER JOIN CategoriesCTE s on a.ParentID = s.ID
)
SELECT * FROM CategoriesCTE
Thing is, this database call saves everything flat. What I would have to do, is that for EACH step, save all urls, and then for each ID, save the url according to what the ParentID is. Right now it of course isn't formatted but my output is flatly something like:
mysite.com/categories
mysite.com/movies
mysite.com/star-wars
mysite.com/a-good-book
Which creates a lot of broken links.
Is there some way to do an action/select for each recursive step? How should I be approaching this problem?
Add a few of new fields to your recursive CTE to track:
Depth of recursion (so you can find the record with the greatest depth
The path which will be built through each iteration by concatenating the latest value to it.
The starting point of the recursion so you know what record you started with
WITH CategoriesCTE AS
(
SELECT Name, Url, ParentID, ID, 1 as depth, CAST(url as VARCHAR(500)) as path, url as startingpoint
FROM myDB
WHERE ParentID = 1
UNION ALL
SELECT a.Name, a.Url, a.ParentID, a.ID, s.depth + 1, a.url + s.path, s.url
FROM myDB.a
INNER JOIN CategoriesCTE s on a.ParentID = s.ID
)
SELECT * FROM CategoriesCTE
See what you think of this...
IF OBJECT_ID('tempdb..#SomeTable', 'U') IS NOT NULL
DROP TABLE #SomeTable;
CREATE TABLE #SomeTable (
ID INT NOT NULL,
ParentID INT NOT NULL,
FolderName VARCHAR(20) NOT NULL,
UrlPath VARCHAR(8000) NULL
);
INSERT #SomeTable (ID, ParentID, FolderName) VALUES
(1 , 0 , 'categories'),
(34 , 1 , 'movies'),
(281, 34, 'star-wars'),
(33 , 1 , 'a-good-book');
-- SELECT * FROM #SomeTable st;
WITH
cte_Categories AS (
SELECT
SitePath = CAST(CONCAT('mysite.com/', st.FolderName) AS VARCHAR(8000)),
st.ID,
NodeLevel = 1
FROM
#SomeTable st
WHERE
st.ParentID = 0
UNION ALL
SELECT
SitePath = CAST(CONCAT(c.SitePath, '/', st.FolderName) AS VARCHAR(8000)),
st.ID,
nodeLevel = c.NodeLevel + 1
FROM
cte_Categories c
JOIN #SomeTable st
ON c.ID = st.ParentID
)
SELECT
c.SitePath,
c.ID,
c.NodeLevel
FROM
cte_Categories c;

MySQL Column Value Pivot

I have a MySQL InnoDB table laid out like so:
id (int), run_id (int), element_name (varchar), value (text), line_order, column_order
`MyDB`.`MyTable` (
`id` bigint(20) NOT NULL,
`run_id` int(11) NOT NULL,
`element_name` varchar(255) NOT NULL,
`value` text,
`line_order` int(11) default NULL,
`column_order` int(11) default NULL
It is used to store data generated by a Java program that used to output this in CSV format, hence the line_order and column_order.
Lets say I have 4 entries (according to the table description):
1,1,'ELEMENT 1','A',0,0
2,1,'ELEMENT 2','B',0,1
3,1,'ELEMENT 1','C',1,0
4,1,'ELEMENT 2','D',1,1
I want to pivot this data in a view for reporting so that it would look like more like the CSV would, where the output would look this:
---------------------
|ELEMENT 1|ELEMENT 2| <--- Element Name
---------------------
| A | B | <--- Value From line_order = 0
---------------------
| C | D | <--- Value From line_order = 1
---------------------
The data coming in is extremely dynamic; it can be in any order, can be any of over 900 different elements, and the value could be anything. The Run ID ties them all together, and the line and column order basically let me know where the user wants that data to come back in order. I want it to sort/group by line and column order in the displayed matrix.
You can do that with a self join:
select e1.element_name, e1.value, e2.value
from MyTable e1
inner join MyTable e2
on e1.element_name = e2.element_name
and e2.line_order = 1
where e1.line_order = 0
The from selects line_order = 1, the inner join selects line_order = 2.
Switch inner join to cross join if you'd like to return elements with only one line_order.
In reply to your comment, you could write out a fixed number of elements like:
select
line_order
, max(case when element_name = 'element 1' then value end) as Element1Value
, max(case when element_name = 'element 2' then value end) as Element2Value
, max(case when element_name = 'element 3' then value end) as Element3Value
, ...
from MyTable
group by line_order
The max() construct assumes that (element_name, line_order) is unique.