I need help getting UNION to behave with JOIN - tsql

I'm trying to generate a report that gives me data from 3 different tables. I used a UNION first, now I need to get one column from the last table. I tried a JOIN but it breaks my code.
Here's what I want:
Select document number, patient full name (in one column), patient account number where patient zip = ‘45142’ then Add location of claim (trans or history) after you get the first part.
This is the bulk of my data
(SELECT DOCUMENT_NUMBER, TRANS_TYPE, PATIENT_LAST_NAME + ', ' + PATIENT_FIRST_NAME AS NAME,PATIENT_ZIP
FROM HCFA_M
WHERE patient_zip like '45142%')
UNION
(SELECT DOCUMENT_NUMBER, TRANS_TYPE, PATIENT_LAST_NAME + ', ' + PATIENT_FIRST_NAME AS NAME,PATIENT_ZIP
FROM UB_M
WHERE patient_zip like '45142%')
ORDER BY NAME asc, TRANS_TYPE
The table I need the last colum from is
SELECT LOCATION
FROM DOCUMENT_M

Going to guess on join condition
SELECT DOCUMENT_NUMBER, TRANS_TYPE, PATIENT_LAST_NAME + ', ' + PATIENT_FIRST_NAME AS NAME,PATIENT_ZIP, LOCATION
FROM HCFA_M
JOIN DOCUMENT_M
ON DOCUMENT_M.DOCUMENT_NUMBER = HCFA_M.DOCUMENT_NUMBER
WHERE patient_zip like '45142%'

Related

Join a table with a specific condition

I have three tables:
"user" who has one or more rows associated with him in the "contractYear" table, which consists of rows in a "contract_month" table.
I want to get the result set of users who do not have contract months for the current contract year (i.e. contract Year.endDate < current date)
I made the following query:
#Query("select distinct u "
+ " from User u "
+ " left join u.contractYears cy"
+ " on cy.endDate < now()"
+ " left join cy.contractMonths cm"
+ " where cm is null")
But it doesn't work...
I assume the condition "on cy.endDate < now()" is not correct.
Maybe someone can help me make the correct query?
In HQL if you need additional filter in join clause you can add using WITH keyword
select distinct u
from User u
left join u.contractYears cy
with cy.endDate < now()
left join cy.contractMonths cm
where cm is null
See 14.3. Associations and joins section

SQL Query to return Sum of items by user and date

I have an Express Api, using postgresql.
I currently use the following query to populate a leaderboard:
SELECT acts.users_id, username, avatar_url, COUNT(acts.id)
FROM acts
RIGHT JOIN users ON acts.users_id = users.id
JOIN memberships on memberships.users_id = users.id
WHERE memberships.groups_id = ' + req.params.group_id + '
GROUP BY acts.users_id, username, avatar_url
ORDER BY COUNT(acts.id) DESC
This returns a list of users all belonging to a specific group listed in descending order of the number of "acts" by each user.
I am trying to modify this query to only include acts that were created within the last eg 7 days.
I have tried this modified query:
SELECT acts.users_id, acts.created_at, username, avatar_url, COUNT(acts.id)
FROM acts
RIGHT JOIN users ON acts.users_id = users.id
JOIN memberships on memberships.users_id = users.id
WHERE memberships.groups_id = ' + req.params.group_id + '
AND acts.created_at >= (CURRENT_DATE - 7)
GROUP BY acts.users_id, acts.created_at, username, avatar_url
ORDER BY COUNT(acts.id) DESC
this modified query returns the correct data, but it separates by date, so I get multiple rows for each user instead of totals by user. I assume this is due to including created_at within group_by. However, if I remove it, I get the error:
error: column "acts.created_at" must appear in the GROUP BY clause or be used in an aggregate function
SELECT acts.users_id, username, avatar_url, COUNT(acts.id)
FROM acts
RIGHT JOIN users ON acts.users_id = users.id
JOIN memberships on memberships.users_id = users.id
WHERE memberships.groups_id = ' + req.params.group_id + '
AND acts.created_at >= (CURRENT_DATE - 7)
GROUP BY acts.users_id, username, avatar_url
ORDER BY COUNT(acts.id) DESC
You do not need to group by a field or select a field for filtering by this field. Just add the condition and do not change anything else.
This is what #forpas meant, I guess.

SQL Server 2008 R2 query related to replacement of data

I have a scenario wherein I have to remove all the strings except a or b or c
My sample table is as follows:
Id Product
------------------
1. a,b,Da,c
2. Ty,a,b,c
3. a,sds,b
Sample output
Id Product
----------------
1. a,b,c
2. a,b,c
3. a,b
My current version is Microsoft SQL Server 2008 R2
This should help you out. As I state in the comments, I make use of Jeff Moden's DelimitedSplit8k, as you're using an older version of SQL Server. if you were using 2016+, you would have access to STRING_SPLIT. I also normalise your data; as storing delimited data is almost always a bad idea.
CREATE TABLE #Sample (id int, Product varchar(20));
INSERT INTO #Sample
VALUES (1,'a,b,Da,c'),
(2,'Ty,a,b,c'),
(3,'a,sds,b');
GO
--The first problem you have is you're storing delimited data
--You really should be storing each item on a separate row.
--This is, however, quite easy to do. i'm going to use a different
--table, however, you can change this fairly easily for your
--needs.
CREATE TABLE #Sample2 (id int, Product varchar(2));
GO
--You can split the data out by using a Splitter.
--My personal preference is Jeff Moden's DelimitedSplit8K
--which I've linked to above.
INSERT INTO #Sample2 (id, Product)
SELECT id, Item AS Product
FROM #Sample S
CROSS APPLY dbo.DelimitedSplit8K(S.Product,',') DS
WHERE DS.Item IN ('a','b','c');
GO
--And hey presto! Your normalised data, and without the unwanted values
SELECT *
FROM #Sample2;
GO
DROP TABLE #Sample;
DROP TABLE #Sample2;
If you have to keep the delimited format, you can use STUFF and FOR XML PATH:
WITH Split AS(
SELECT id,
Item AS Product,
ItemNumber
FROM #Sample S
CROSS APPLY dbo.DelimitedSplit8K(S.Product,',') DS
WHERE DS.Item IN ('a','b','c'))
SELECT id,
STUFF((SELECT ',' + Product
FROM Split sq
WHERE sq.id = S.id
ORDER BY ItemNumber
FOR XML PATH('')),1,1,'')
FROM Split S
GROUP BY id;
This also will do the thing, using xml only:
select * into #t from (values('a,b,Da,c'),('Ty,a,b,c'),('a,sds,b'))v(Product)
;
with x as (
SELECT t.Product, st.sProduct
FROM #t t
cross apply (
SELECT CAST(N'<root><r>' + REPLACE(t.Product,',', N'</r><r>') + N'</r></root>' as xml) xProduct
)xt
cross apply (
select CAST(r.value('.','NVARCHAR(MAX)') as nvarchar) sProduct
from xt.xProduct.nodes(N'//root/r') AS RECORDS(r)
) st
where st.sProduct in ('a', 'b', 'c')
)
select distinct x.Product, REVERSE(SUBSTRING(REVERSE(cleared.cProduct), 2, 999)) cleared
from x
cross apply ( select (
select distinct ref.sProduct + ','
from x ref
where ref.Product = x.Product
for xml path('') )
)cleared(cProduct)
;
drop table #t

Row concatenation with FOR XML, but with multiple columns?

I often use queries like:
SELECT *
FROM ThisTable
OUTER APPLY (SELECT (SELECT SomeField + ' ' AS [data()]
FROM SomeTable
WHERE SomeTable.ID = ThisTable.ID
FOR XML PATH ('')) AS ConcatenatedSomeField) A
I often want to get multiple concatenated concatenated fields from this table, instead of just one. I could logically do this:
SELECT *
FROM ThisTable
OUTER APPLY (SELECT (SELECT SomeField + ' ' AS [data()]
FROM SomeTable
WHERE SomeTable.ID = ThisTable.ID
FOR XML PATH ('')) AS ConcatenatedSomeField) A
OUTER APPLY (SELECT (SELECT SomeField2 + ' ' AS [data()]
FROM SomeTable
WHERE SomeTable.ID = ThisTable.ID
FOR XML PATH ('')) AS ConcatenatedSomeField2) B
OUTER APPLY (SELECT (SELECT SomeField3 + ' ' AS [data()]
FROM SomeTable
WHERE SomeTable.ID = ThisTable.ID
FOR XML PATH ('')) AS ConcatenatedSomeField3) C
But it looks crappy and error prone when anything needs to be updated; also SomeTable is often a long list of joined tables so it could also have performance implications getting the same tables over and over.
Is there a better way to do this?
Thanks.
You could do something like this. Instead of immediately sending the XML value to a string, this query uses the TYPE keyword to return an xml type object which can then be queried. The three query functions search the xml object for all instances of the Somefield element and return a new xml object containing just those values. Then the value function strips out the xml tags surrounding the values and passes them into a varchar(max)
SELECT ThisTable.ID
,[A].query('/Somefield').value('/', 'varchar(max)') AS [SomeField_Combined]
,[A].query('/Somefield2').value('/', 'varchar(max)') AS [SomeField2_Combined]
,[A].query('/Somefield3').value('/', 'varchar(max)') AS [SomeField3_Combined]
FROM ThisTable
OUTER APPLY (
SELECT (
SELECT SomeField + ' ' AS [SomeField]
,SomeField2 + ' ' AS [SomeField2]
,SomeField3 + ' ' AS [SomeField3]
FROM SomeTable
WHERE SomeTable.ID = ThisTable.ID
FOR
XML PATH('')
,TYPE
) AS [A]
) [A]
You can create a CLR User-Defined Aggregate Function that does the concatenation for you.
Your code would then look like this instead.
select S.ID,
dbo.Concat(S.SomeField1),
dbo.Concat(S.SomeField2),
dbo.Concat(S.SomeField3)
from SomeTable as S
group by S.ID
This is the same answer as I gave here: https://dba.stackexchange.com/questions/125771/multiple-column-concatenation/
The OP of that question referenced the answer given here. You can see below that sometimes the simplest answer can be the best. If SomeTable is multiple tables then I would go ahead and put it into a CTE to avoid having the same complex code multiple times.
I ran a few tests using a little over 6 mil rows. With an index on the ID column.
Here is what I came up with.
Your initial query:
SELECT * FROM (
SELECT t.id,
stuff([M].query('/name').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined1],
stuff([M].query('/car').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined2]
FROM dbo.test t
OUTER APPLY(SELECT (
SELECT id, ','+name AS name
,','+car AS car
FROM test WHERE test.id=t.id
FOR XML PATH('') ,type)
AS M)
M ) S
GROUP BY id, SomeField_Combined1, SomeField_Combined2
This one ran for ~23 minutes.
I ran this version which is the version I first learned. In some ways it seems like it should take longer but it doesn't.
SELECT test.id,
STUFF((SELECT ', ' + name
FROM test ThisTable
WHERE test.id = ThisTable.id
FOR XML PATH ('')),1,2,'') AS ConcatenatedSomeField,
STUFF((SELECT ', ' + car
FROM test ThisTable
WHERE test.id = ThisTable.id
FOR XML PATH ('')),1,2,'') AS ConcatenatedSomeField2
FROM test
GROUP BY id
This version ran in just over 2 minutes.

T-SQL Query, combine columns from multiple rows into single column

I have seeen some examples of what I am trying to do using COALESCE and FOR XML (seems like the better solution). I just can't quite get the syntax right.
Here is what I have (I will shorten the fields to only the key ones):
Table Fields
------ -------------------------------
Requisition ID, Number
IssuedPO ID, Number
Job ID, Number
Job_Activity ID, JobID (fkey)
RequisitionItems ID, RequisitionID(fkey), IssuedPOID(fkey), Job_ActivityID (fkey)
I need a query that will list ONE Requisition per line with its associated Jobs and IssuedPOs. (The requisition number start with "R-" and the Job Number start with "J-").
Example:
R-123 | "PO1; PO2; PO3" | "J-12345; J-6780"
Sure thing Adam!
Here is a query that returns multiple rows. I have to use outer joins, since not all Requisitions have RequisitionItems that are assigned to Jobs and/or IssuedPOs (in that case their fkey IDs would just be null of course).
SELECT DISTINCT Requisition.Number, IssuedPO.Number, Job.Number
FROM Requisition
INNER JOIN RequisitionItem on RequisitionItem.RequisitionID = Requisition.ID
LEFT OUTER JOIN Job_Activity on RequisitionItem.JobActivityID = Job_Activity.ID
LEFT OUTER JOIN Job on Job_Activity.JobID = Job.ID
LEFT OUTER JOIN IssuedPO on RequisitionItem.IssuedPOID = IssuedPO.ID
Here's one way to do it using subqueries:
select 'R-' + cast(r.number as varchar(32)) as RequisitionNumber
, (
select 'PO' + CAST(ip.number as varchar(32)) + ';'
from IssuedPO ip
join RequisitionItems ri
on ip.id = ri.IssuedPOID
where ri.RequisitionID = r.id
for xml path('')
) as POList
, (
select 'J-' + CAST(j.number as varchar(32)) + ';'
from Job j
join Job_Activity ja
on j.id = ja.JobID
join RequisitionItems ri
on ri.Job_ActivityID = ja.id
where ri.RequisitionID = r.id
for xml path('')
) as JobList
from Requisition r