Left outher join count rows from different table minus value different row - sql

Hello I have following SQL query
SELECT K.name AS Name, K.surname AS Surname, U1.akce AS Event,
U2.[text] AS [Scheme], U1.[text] AS [Registered under>],
( U1.x - (
SELECT Count(K.ubytov)
FROM klient
WHERE ubytov = U2.[text]) ) AS [Free space]
FROM klient K
INNER JOIN ubytov U1 ON U1.[text] = K.ubytov
LEFT OUTER JOIN ubytov U2 ON U1.z = U2.id WHERE U1.akce = '140012-02'
ORDER BY U1.[text]
I'm trying to achieve that in column Free space would be (value from ubytov.x that matches U1.z = U2.id) - (total number of rows from table klient that has the same value in U1.[text]=K.ubytov)
In table klient column ubytov I have values that matches ubytov.text and in ubytov.z I have value that matches ubytov.x in different row.
Would somebody help me solve this out please?
Thank you for your time.
An example:
Table klient
ID_K ubytov
1 RoomOwner
2 RoomOwner
table ubytov
id text x z
1 roomType1 2 NULL
2 RoomOwner NULL 1
Desired Output:
Name Surname Event Scheme Registered under: Free space
Nam1 Surname1 Even1 Scheme1 RoomOwnerName 0 // (because 2 counts from klient) - (roomType1 x)

Although, it wasn't much clear because of missing columns.
I tried to build the required query using a CTE expression.
Here is the sqlfiddle code.
Let me know, if this is what you are looking for.
create table klient
(
id_k int,
ubytov varchar(25)
)
go
create table ubytov
(
id int,
text varchar(25),
x int null,
z int null
)
go
insert into klient(id_k, ubytov)
select 1, 'RoomOwner'
union select 2, 'RoomOwner'
go
insert into ubytov(id, text, x, z)
select 1, 'roomType1', 2, null
union select 2, 'RoomOwner', null, 1
go
;WITH cte_klint_counts_by_ubytov
AS
(
SELECT
ubytov,
Count(ubytov) AS ubytovCount
FROM klient
GROUP BY ubytov
)
SELECT
U2.[text] AS [Scheme],
U1.[text] AS [Registered under>],
(isnull(U1.x, 0) - isnull(c.ubytovCount, 0)) AS [Free space]
FROM
klient K
INNER JOIN ubytov U1 ON U1.[text] = K.ubytov
LEFT OUTER JOIN ubytov U2 ON U1.z = U2.id
LEFT OUTER JOIN cte_klint_counts_by_ubytov c ON c.ubytov = U2.[text]
ORDER BY u1.[text]

Related

Combining a recursive CTE with another query

I have a table of locations each of which can have a parent location
LocationId | ParentLocationId
-----------------------------
1 null
2 1
3 2
4 2
I managed to create a recursive CTE which gives me parent location id (plus the original location id) for any given location id
WITH GetLocationParents AS
(
select [LocationId], [ParentLocationId] from Locations
where LocationId = 3
UNION ALL
select i.[LocationId], i.[ParentLocationId]
from Locations i
join GetLocationParents cte on cte.ParentLocationId = i.LocationId
)
SELECT [ParentLocationId] FROM GetLocationParents
WHERE [ParentLocationId] is not NULL;
e.g. where LocationId = 3 would return:
ParentLocationId
----------------
3
2
1
In another table I have a query which will return LocationId as one of the fields:
select exi.PersonId, exi.LocationId from Persons e
left join PersonHasLocations exi on e.PersonId = exi.PersonId
left join Locations i on exi.LocationId = i.LocationId
Which with a where clause would return something like:
PersonId | LocationId
---------------------
100 3
I'm trying to combine these queries to get the result:
PersonId | LocationId
---------------------
100 3
100 2
100 1
I'm trying the following but it's still only returning the first row:
WITH
GetLocationParents AS
(select [LocationId], [ParentLocationId] from Locations
--where LocationId = 3
UNION ALL
select i.[LocationId], i.[ParentLocationId]
from Locations i inner join GetLocationParents cte
on cte.ParentLocationId = i.LocationId),
GetPersons AS
(select exi.PersonId, exi.LocationID from Persons e
left join PersonHasLocations exi on e.PersonID = exi.PersonId
left join Locations i on exi.LocationId = i.LocationID)
SELECT * FROM GetLocationParents gip
INNER JOIN GetPersons ge on ge.LocationId = gip.LocationID
WHERE ge.PersonId = 100
Is it possible to merge a recursive query with a normal query like this?
I guess you have a small bug in your cte. I would suggest to change the query as follows:
DECLARE #t TABLE (
LocationId int,
ParentLocationId int
)
INSERT INTO #t VALUES
(1, NULL)
,(2, 1)
,(3, 2)
,(4, 2)
;WITH GetLocationParents AS
(
select [LocationId] AS k, [LocationId], [ParentLocationId] from #t
UNION ALL
select k, i.[LocationId], i.[ParentLocationId]
from GetLocationParents cte
join #t i on cte.ParentLocationId = i.LocationId
)
SELECT *
FROM GetLocationParents
WHERE k = 3
With this you receive a list with the value you filter on in the first column and all depending "levels" above this in the second column. This can then be used in order to join to your second table.
Keep in mind that - depending on your number of levels - you will have to take care of MAX RECUSRSION.

Use Data of 1 table into another one dynamically

I have one table category_code having data like
SELECT Item, Code, Prefix from category_codes
Item Code Prefix
Bangles BL BL
Chains CH CH
Ear rings ER ER
Sets Set ST
Rings RING RG
Yellow GOld YG YG........
I have another table item_categories having data like
select code,name from item_categories
code name
AQ.TM.PN AQ.TM.PN
BL.YG.CH.ME.PN BL.YG.CH.ME.PN
BS.CZ.ST.YG.PN BS.CZ.ST.YG.PN
CR.YG CR.YG.......
i want to update item_categories.name column corresponding to category_code.item column like
code name
BL.YG.CH.ME.PN Bangles.Yellow Gold.Chains.. . . .
Please suggest good solution for that. Thanks in advance.
First, split the code into several rows, join with the category code and then, concat the result to update the table.
Here an example, based on the data you gave
create table #category_code (item varchar(max), code varchar(max), prefix varchar(max));
create table #item_categories (code varchar(max), name varchar(max));
insert into #category_code (item, code, prefix) values ('Bangles','BL','BL'),('Chains','CH','CH'),('Ear rings','ER','ER'), ('Sets','Set','ST'),('Rings','RING','RG'), ('Yellow gold','YG','YG');
insert into #item_categories (code, name) values ('AQ.TM,PN','AQ.TM.PN'),('BL.YG.CH.ME.PN','BL.YG.CH.ME.PN'),('BS.CZ.ST.YG.PN','BS.CZ.ST.YG.PN')
;with splitted as ( -- split the codes into individual code
select row_number() over (partition by ic.code order by ic.code) as id, ic.code, x.value, cc.item
from #item_categories ic
outer apply string_split(ic.code, '.') x -- SQL Server 2016+, otherwise, use another method to split the data
left join #category_code cc on cc.code = x.value -- some values are missing in you example, but can use an inner join
)
, joined as ( -- then joined them to concat the name
select id, convert(varchar(max),code) as code, convert(varchar(max),coalesce(item + ',','')) as Item
from splitted
where id = 1
union all
select s.id, convert(varchar(max), s.code), convert(varchar(max), j.item + coalesce(s.item + ',',''))
from splitted s
inner join joined j on j.id = s.id - 1 and j.code = s.code
)
update #item_categories
set name = substring (j.item ,1,case when len(j.item) > 1 then len(j.item)-1 else 0 end)
output deleted.name, inserted.name
from #item_categories i
inner join joined j on j.code = i.code
inner join (select code, max(id)maxid from joined group by code) mj on mj.code = j.code and mj.maxid = j.id

SQL: performantly find the percent overlap between two foreign key child tables

Assume I have the following tables/fields:
CREATE TABLE tbl_projects (
prjc_id int PRIMARY KEY
)
CREATE TABLE tbl_project_requirements (
preq_prjc_id int -- Foreign key to tbl_projects
preq_type_id int -- A standardized requirement category
)
Given a specific project, I would like to find other projects that have nearly similar requirement categories... or let's say at least a 75% overlap on their requirements.
I could do the following:
DECLARE #prjc_id int = 1
CREATE TABLE #project_reqs (type_id int)
INSERT INTO #project_reqs
SELECT preq_req_type_id
FROM tbl_project_requirements
WHERE preq_prjc_id = #prjc_id
SELECT prjc_id
FROM tbl_projects
CROSS APPLY (
SELECT CASE
WHEN COUNT(*) = 0 THEN 0.0
ELSE COALESCE(SUM(CASE WHEN type_id = prjc_type_id THEN 1.0 ELSE 0.0 END), 0.0)
/ CONVERT(float, COUNT(*))
END AS similarity
FROM #project_reqs
FULL OUTER JOIN (
SELECT prjc_type_id
FROM tbl_project_requirements
WHERE preq_prjc_id = prjc_id
) reqs ON preq_type_id = type_id
) reqs
WHERE prjc_id != #prjc_id
AND similarity >= 0.75
In the above, I'm dividing the matched requirement categories by the total distinct requirement categories between each two projects to get the % overlap.
While this works, I sense code smells, and don't think this will scale very well. Is there any sort of method that exists to performantly calculate overlap of child records between two items? Maybe some sort of partial hash matching or...?
Update
I think I found a performant solution:
DECLARE #prjc_id int = 1
CREATE TABLE #project_reqs (type_id int)
INSERT INTO #project_reqs
SELECT preq_req_type_id
FROM tbl_project_requirements
WHERE preq_prjc_id = #prjc_id
DECLARE #project_req_count float
SELECT #project_req_count = COUNT(*)
FROM #project_reqs
CREATE TABLE #projects (
pj_prjc_id int,
pj_func_count float,
pj_func_common float
)
INSERT INTO #projects
SELECT preq_prjc_id,
COUNT(*),
COUNT(type_id)
FROM tbl_project_requirements
LEFT OUTER JOIN #project_reqs
ON preq_type_id = type_id
GROUP BY preq_prjc_id
HAVING COUNT(type_id) != 0
SELECT pj_prjc_id
FROM #projects
WHERE pj_func_common / (pj_func_count + #project_req_count - pj_func_common) >= 0.75
DROP TABLE #project_reqs
DROP TABLE #projects
There is more elegant way to find common requirements.
;with proj as (
select preq_prjc_id pr, count(preq_type_id) typeCnt
from tbl_project_requirements
group by preq_prjc_id
)
,crossProj as (
select p1.pr proj1,p2.pr proj2, p1.typeCnt
from proj p1
cross join proj p2 --make Cartesian product
where p1.pr <> p2.pr
)
,req as (
select preq_type_id, cp.proj1, cp.proj2, cp.typeCnt
from tbl_project_requirements pq
inner join crossProj cp on pq.preq_prjc_id=cp.proj1
intersect -- what is common
select preq_type_id, cp.proj1, cp.proj2, cp.typeCnt
from tbl_project_requirements pq
inner join crossProj cp on pq.preq_prjc_id=cp.proj2
)
--calculate final result
select proj1, proj2,
count(preq_type_id) commonPreq,
--percent of common requirements relative to proj1
count(preq_type_id) * 100.00 / typeCnt [percentage]
from req
group by proj1, proj2, typeCnt
having count(preq_type_id) * 100.00 / typeCnt >75
order by [percentage] desc
Update
;with proj as (
select preq_prjc_id pr, count(preq_type_id) typeCnt
from tbl_project_requirements
group by preq_prjc_id
)
,crossProj as (
select p1.pr proj1,p2.pr proj2, p1.typeCnt
from proj p1
cross join proj p2 --make Cartesian product
where p1.pr <> p2.pr
)
,req as (
select preq_type_id, cp.proj1, cp.proj2
from tbl_project_requirements pq
inner join crossProj cp on pq.preq_prjc_id=cp.proj1
intersect -- what is common
select preq_type_id, cp.proj1, cp.proj2
from tbl_project_requirements pq
inner join crossProj cp on pq.preq_prjc_id=cp.proj2
)
--calculate final result
select proj1, proj2,
count(preq_type_id) commonPreq,
--percent of common requirements relative to proj1
count(preq_type_id) * 100.00 /(p1.typeCnt + p2.typeCnt - count(preq_type_id)) [percentage]
from req
inner join proj p1 on req.proj1=p1.pr
inner join proj p2 on req.proj2=p2.pr
group by proj1, proj2,p1.typeCnt, p2.typeCnt
having count(preq_type_id) * 100.00 /(p1.typeCnt + p2.typeCnt - count(preq_type_id)) >75
order by [percentage] desc
If you're only doing this for one project then you should already know the number of matches that are required for a 75% match (or at least you can calculate it easily and quickly) enough:
DECLARE #num_matches_required INT
SELECT #num_matches_required = CEILING(COUNT(*) * 0.75)
FROM
tbl_Project_Requirements
WHERE
preq_prjc_id = #preq_prjc_id
SELECT
R2.preq_prjc_id -- One reason not to use abbreviations... I have to think, "Is it proj? prj? prjc? prjct?"
FROM
tbl_Project_Requirements R1
INNER JOIN tbl_Project_Requirements R2 ON
R2.preq_type_id = R1.preq_type_id AND
R2.preq_prjc_id <> #preq_prjc_id
WHERE
R1.preq_prjc_id = #preq_prjc_id
GROUP BY
R2.preq_prjc_id
HAVING
COUNT(*) >= #num_matches_required

Left Join not matching where data exists - Temp Tabls SQL 2012

I have a query that I am using to pull in data from different tables. I have broken downthe query into a few different parts to make like a little easier. The problem is that when an ID number exists for sure in both places the LEFT JOIN does not always match them up, somtimes it does sometimes it does not.
All of the ID numbers are of type INT so I know all the data types are the same. I tried to use COLLATE SQL_Latin_General_Pref_CP1_CI_AS in the join statement but that is invalid for data of type INT was the error I received. OK fine, but I am really stuck as I don't know where to proceede from here other than dumping into excel and doing a vlookup.
Here is what I have:
DECLARE #sd DATETIME;
DECLARE #ed DATETIME;
SET #sd = '2014-01-01';
SET #ed = '2015-10-01';
DECLARE #denials_write_offs TABLE (
pk INT IDENTITY(1, 1) PRIMARY KEY
, pt_id INT
, bill_no INT
, denials FLOAT
)
INSERT INTO #denials_write_offs
SELECT a.pt_id
, a.bill_no
, a.denials_woffs
FROM (
SELECT CAST(pt_id AS INT) pt_id
, CAST(bill_no AS INT) bill_no
, SUM(tot_pay_adj_amt) AS denials_woffs
FROM smsmir.mir_pay
JOIN smsdss.c_Softmed_Denials_Detail_v
ON smsmir.mir_pay.pt_id = smsdss.c_Softmed_Denials_Detail_v.bill_no
WHERE discharged >= #sd
AND discharged < #ed
AND LEFT(smsmir.mir_pay.pay_cd, 4) = '0974'
GROUP BY pt_id
, bill_no
) A
--------------------------------------
DECLARE #EDTBL TABLE (
ACCOUNT INT
, ED_MD VARCHAR(MAX)
)
INSERT INTO #EDTBL
SELECT Z.*
FROM (
SELECT CAST(ACCOUNT AS INT) ACCOUNT
, ED_MD
FROM SMSDSS.c_Wellsoft_Rpt_tbl
) Z
----------------------------------------
DECLARE #TmpDenialsTbl TABLE (
PK INT IDENTITY(1, 1) PRIMARY KEY
, BILL_NO INT
...
...
...
)
INSERT INTO #TmpDenialsTbl
SELECT *
FROM (
SELECT bill_no
...
...
...
)
So as you can see, from the above every pt_id or bill_no is put into a table as an INT but I cannot for the life of me figure out why sometimes I get a match on my LEFT JOIN and why sometimes I do not. I did pull data from the #EDMD table and get the account number I was looking for but it did not hit on the left join below:
FROM #TmpDenialsTbl A
LEFT OUTER JOIN #denials_write_offs D
ON A.bill_no = d.pt_id
LEFT OUTER JOIN #EDTBL C
ON C.Account = D.bill_no
LEFT OUTER JOIN #USERTBL F
ON A.CERM_RVWR_ID = F.login_id
AND F.RN = 1
Example of what I am doing and what I get back
DECLARE #TmpDenials TABLE (
PT_ID INT
)
INSERT INTO #TmpDenials
SELECT A.*
FROM (
SELECT CAST(PT_ID AS INT) PT_ID
FROM SOME_TABLE
) A
DECLARE #EDMD TABLE (
PT_ID INT
EDMD VARCHAR(MAX)
)
INSERT INTO #EDMD
SELECT B.*
FROM (
SELECT CAST(PT_ID AS INT) PT_ID
EDMD
FROM SOME_OTHER_TABLE
)B
SELECT * FROM #TmpDenials
PT_ID |
123456789 |
SELECT * FROM #EDMD
PT_ID | ED_MD
123456789 | Dr. Emergency Room
SELECT *
FROM #TmpDenials A
LEFT OUTER JOIN #EDMD B
ON A.PT_ID = B.PT_ID
A.PT_ID | B.PT_ID | ED MD
123456789 | NULL | NULL
I'm not sure if this is the cause of your problem or not, but this condition is potentially suspicious:
FROM #TmpDenialsTbl A LEFT OUTER JOIN
#denials_write_offs D
ON A.bill_no = d.pt_id LEFT OUTER JOIN
#EDTBL C
ON C.Account = D.bill_no LEFT OUTER JOIN
--------^
#USERTBL F
ON A.CERM_RVWR_ID = F.login_id AND F.RN = 1
If there is no match to the C table form A, then this will always fail to match. I'm not sure if this is the intended behavior.
The other option is that a WHERE clause is filtering out the rows that you want.
Note: When you use table aliases, you should use abbreviations for the table names, so i, wo, e, and u are better than the arbitrary letters a, d, c, and f.

Return first ID matching a sequence of conditions

Create and Populate POC tables:
CREATE TABLE #POCSTBL (SCode varchar(10))
INSERT INTO #POCSTBL (SCode)
SElECT 'LC13'
UNION ALL
SELECT 'LC22'
UNION ALL
SELECT 'LC31'
CREATE TABLE #POCLUTBL (ID int Identity (1,1), LC1 varchar(10), LC2 varchar(10), LC3 varchar(10))
INSERT INTO #POCLUTBL (LC1, LC2, LC3)
SELECT 'LC11',NULL,'LC13'
UNION ALL
SELECT 'LC21','LC13',NULL
UNION ALL
SELECT '-','LC31','LC33'
SELECT * FROM #POCSTBL
SELECT * FROM #POCLUTBL
My Initial Try:
SELECT S.SCode, LU.ID FROM #POCSTBL S LEFT JOIN #POCLUTBL LU
ON S.SCode = LU.LC1 OR S.SCode = LU.LC2 OR S.SCode = LU.LC3
Drop #Tbls:
DROP TABLE #POCSTBL
DROP TABLE #POCLUTBL
Desired Output:
SCode ID
LC13 2
LC22 NULL
LC33 3
I want to lookup SCode column from #POCSTBL against #POCLUTBL's
LC2 first, if found, take that ID as output,
if ID not found then LC1, if found, take that ID as output,
if ID not found then LC3, if found, take that ID as output,
if ID not found then ID = NULL
Thank you
Like this:
SELECT
S.SCode,
COALESCE(L1.ID, L2.ID, L3.ID) As ID
FROM #POCSTBL S
LEFT JOIN #POCLUTBL L1 ON S.SCode = L1.LC1
LEFT JOIN #POCLUTBL L2 ON S.SCode = L2.LC2
LEFT JOIN #POCLUTBL L3 ON S.SCode = L3.LC3
Assuming that there are not potential duplicate SCodes in LC1, LC2 or LC3. If there may be, then you will need a more complicated version of this.