Selecting from two queries select not null - sql

i have query that the result is a single value there are many cases that bring me a null value in this case that's what i not need so i need make another query to bring me back a value, so i need to make a one query that bring me back when is null value in the first query omit the result and execute the second query.
the firts query is
SELECT DISTINCT
FIRST_VALUE (pac1.pac_name)
OVER (ORDER BY pac1.pac_final_date DESC)
FROM matricula mac
INNER JOIN
periodo pac1
ON mac.pac_id = pac1.pac_id
WHERE mac.ent_id = 26172 AND mac.mac_estado IN (8072, 10221)
the second query is
SELECT DISTINCT
FIRST_VALUE (pac1.pac_name)
OVER (ORDER BY pac1.pac_final_date DESC)
FROM registro rea
INNER JOIN
periodo pac1
ON rea.pac_id = pac1.pac_id
WHERE rea.ent_id = 26172
The two queries bring me back the same value, but first i need to consult for the first query, there are two cases.
case -1 --> when execute query#1 and bring me the result
Result
FIRST_VALUE (pac1.pac_name)
--------------------------
|Oct/2012 - Feb/2013 |
--------------------------
case -2 --> when execute query#1 the result is null, then execute a query #2 that will assure bring me a value
Result
FIRST_VALUE (pac1.pac_name)
--------------------------
|Oct/2012 - Feb/2013 |
--------------------------
This is probably an easy question, but any help is appreciated.

SELECT COALESCE(
(SELECT DISTINCT
FIRST_VALUE (pac1.pac_name)
OVER (ORDER BY pac1.pac_final_date DESC)
FROM matricula mac
INNER JOIN
periodo pac1
ON mac.pac_id = pac1.pac_id
WHERE mac.ent_id = 26172 AND mac.mac_estado IN (8072, 10221) )
,(SELECT DISTINCT
FIRST_VALUE (pac1.pac_name)
OVER (ORDER BY pac1.pac_final_date DESC)
FROM registro rea
INNER JOIN
periodo pac1
ON rea.pac_id = pac1.pac_id
WHERE rea.ent_id = 26172))
I am not positive without test data and a few more things but you could also use periodio as the main table and then LEFT OUTER JOIN to the other 2 tables. Use a case expression to determine which table/rows to use first in your order by. a similar method with EXISTS could also probably be considered:
SELECT DISTINCT FIRST_VALUE(pac1.pac_name) OVER (ORDER BY
CASE WHEN mac.pac_id IS NOT NULL THEN 0 ELSE 1 END, pac1.pac_final_date DESC)
FROM
periodo pac1
LEFT JOIN matricula mac
ON pac1.pac_id = mac.pac_id
AND mac.ent_id = 26172
AND mac.mac_estado IN (8072, 10221)
LEFT JOIN registro rea
ON pac1.pac_id = rea.pac_id
AND rea.ent_id = 26172

Related

Sub-Query Should return Null when comparing it to the Next rows from the different table

My T-SQL subquery returning more than 1 rows:
WITH ReportView AS
(
SELECT
PMA.AssetName, ReleaseDt, ExpiresDt, TicketNumber, ChangeDt,
ChangeReasonCd,
ROW_NUMBER() OVER (PARTITION BY PMA.AssetName, ReleaseDt, ExpiresDt, TicketNumber, ChangeReasonCd
ORDER BY ChangeDt ASC) AS ROW_NUM
FROM
pmm.pmmreleaserequest PRR WITH (nolock)
LEFT JOIN
pmm.PmmManagedAccount AS PMA WITH (nolock) ON PRR.ManagedAccountID = PMA.ManagedAccountID
LEFT JOIN
dbo.ManagedEntity AS ME WITH (nolock) ON PRR.ManagedSystemID = ME.ManagedEntityID
LEFT JOIN
dbo.Asset AS AST WITH (nolock) ON ME.AssetID = AST.AssetID
LEFT JOIN
pmm.PmmLogChange AS PLC WITH (nolock) ON PRR.ManagedAccountID = PLC.ManagedAccountID
AND PRR.ExpiresDt < PLC.ChangeDt
)
SELECT *
FROM ReportView
WHERE ROW_NUM = 1
How to compare current row of ChangeDt with the next row of ReleaseDt. Example ChangeDt(current row) < ReleaseDt(Next row)
How can I put this condition.
This is how row_num is defined:
ROW_NUMBER() OVER (PARTITION BY PMA.AssetName, ReleaseDt, ExpiresDt, TicketNumber, ChangeReasonCd
ORDER BY ChangeDt ASC) AS ROW_NUM
It is going to return one row per unique combination of the columns in the PARTITION BY. That is one row per PMA.AssetName / ReleaseDt / ExpiresDt / TicketNumber / ChangeReasonCd.
I would expect there to be more than one such combination.
If you want only one row, then use SELECT TOP (1) or OFFSET/FETCH.

Duplicate rows when joining tables

I am having troubles with a SQL. My problem is that I get alot of duplicate rows but I don't know how to fix it.
I have the following tables:
tblCGG with columns: listId, description
tblCLA with columns: listid, CLADescription
tblHEA with columns: listid, HEADescription
tblACT with columns: listid, ACTDescription
If I run these tables seperatly with listid = '132623' I get the following output:
tblCGG: 1 row
tblCLA: 4 rows
tblHEA: 10 rows
tblACT: 4 rows
I want to join these tables together, but I am getting way to many rows.
I tried this query below, but I get 160 rows:
select distinct cgg.listid, cla.claDescription, hea.heaDescription,
act.actDescription
from tblCGG cgg
left join tblCLA cla on cgg.listid = cla.listid
left join tblHEA hea on cgg.listid = hea.listid
left join tblACT act on cgg.listid = act .listid
where cgg.listid = '132623'
Desired Output
listid claDescription heaDescription actDescription
132623 claTest hea1 act1
132623 clads hea2 act2
132623 cloas hea3 act3
132623 ccaa hea4 act4
132623 null hea5 null
132623 null hea6 null
132623 null hea7 null
132623 null hea8 null
132623 null hea9 null
132623 null hea10 null
I am not sure if desired output really has sense. But if it is what you really, REALLY need then.
select coalesce(t.listid, c.listid, a.listid, h.listid) listid,
cladescription, headescription, actdescription
from tblcgg t
FULL OUTER join (select a.*, row_number() over(partition by listid order by cladescription) seq_no from tblcla a) c on t.listid=c.listid
FULL OUTER join (select a.*, row_number() over(partition by listid order by actdescription) seq_no from tblact a) a on t.listid=a.listid and a.seq_no=c.seq_no
FULL OUTER join (select a.*, row_number() over(partition by listid order by headescription) seq_no from tblhea a) h on h.listid=a.listid and (h.seq_no=c.seq_no or h.seq_no=a.seq_no)
where coalesce(t.listid, c.listid, a.listid, h.listid)=132623
I am a bit upset with this code as performance will be low on bigger datasets but can't quickly find better solutions without writing function.
Few words of code explanation:
row_number() is window function for obtaining sequence number of each description in each table (you can play with "order by" in it for desired ordering)
full outer join is something that shouldn't be used lightly as performance is not a good side of it but you want a rather strange output so it is good for it
coalesce() returns first not null value
You really should think if union all descriptions will not be better for you:
select listid, 'cgg' source,description from tblcgg where listid=132623
UNION ALL
select listid, 'act' source,actdescription from tblact where listid=132623
UNION ALL
select listid, 'head' source,headescription from tblhea where listid=132623
UNION ALL
select listid, 'cla' source,cladescription from tblcla where listid=132623
You want a separate list in each column. This isn't really a SQL'ish thing to do, but you can arrange it. One method uses row_number() and group by:
select listid, max(claDescription) as claDescription,
max(heaDescription) as heaDescription,
max(actDescription) as actDescription
from ((select cla.listid, cla.claDescription, NULL as heaDescription, NULL as actDescription,
row_number() over (partition by cla.listid order by cla.listid) as seqnum
from tblCLA cla
) union all
(select hea.listid, NULL as claDescription, hea.heaDescription, NULL as actDescription,
row_number() over (partition by hea.listid order by hea.listid) as seqnum
from tblHEA hea
) union all
(select act.listid, NULL as claDescription, NULL as heaDescription, act.actDescription,
row_number() over (partition by act.listid order by act.listid) as seqnum
from tblACT act
)
) x
where listid = 132623 -- only use single quotes if this is really a string
group by listid, seqnum;
The following query will give the results you're looking for. It's a slight mod of your original, but depends on knowing that tblHEA has the most rows in it:
WITH ctecla as (select listid, cladescription, rownum as cla_rownum from tblcla),
ctehea as (select listid, headescription, rownum as hea_rownum from tblhea),
cteact as (select listid, actdescription, rownum as act_rownum from tblact)
select cgg.listid,
cla.claDescription,
hea.heaDescription,
act.actDescription
from tblCGG cgg
left join cteHEA hea
on hea.listid = cgg.listid
left join cteCLA cla
on cla.listid = hea.listid AND
cla.cla_rownum = hea.hea_rownum
left join cteACT act
on act.listid = hea.listid AND
act.act_rownum = hea.hea_rownum
where cgg.listid = '132623';
SQLFiddle here

Only select most recent record (calldatetime) SQL

Im trying to only select the most recent calldatetime and display that in my results, can anyone help? I need the results to only show the latest updated record
SELECT
ROW_NUMBER() OVER (PARTITION BY l.phonenum ORDER BY h.calldatetime) as rn,
h.HistoryID,
l.ProjectID,
h.ProjName,
l.CompanyName,
l.Phonenum,
l.fname,
l.lname,
l.Address1,
l.Address2,
l.Address3,
l.Town,
l.Postcode,
l.county,
h.CallDateTime,
cb.CBDatetime,
l.apptdate,
l.appttime,
h.CRC,
c.Description,
a.Firstname + ' ' + a.Lastname AS AgentName,
l.Notes
FROM
History h inner join
cmp_UtilityTrade l on h.PhoneNum = l.Phonenum left outer join
CRC c on c.CRC = h.CRC left outer join
Agent a on a.AgentID = h.AgentID left outer join
CallBack cb on cb.DialID = h.DialID
WHERE
h.calldatetime BETWEEN '2016-05-01 00:00:00' AND '2016-06-15 23:00:00'
ORDER BY
l.Phonenum
It looks like you already have the row_number() over window function setup the way you need it. Assuming it's returning the correct value, you simply need to filter your results by that rn value. Just place your existing query in a CTE, and filter the results like so:
with cte as (<your_query_without_order_by_phonenum>)
select *
from cte
where rn = 1
order by phonenum
Also note that you probably want to move the order by l.Phonenum clause outside the CTE, and place it in the outer query.
EDIT
As noted in the comments, you may also have to fix the order by clause inside the row_number() window function to h.calldatetime DESC.

How to find a record in another table recorded at the nearest time in a subquery aggregate

select sl.*,
(select pnd_invoiceno
from PINVDET
where PND_INVNO = sl.invno and
abs(DATEDIFF(ss, pnd_date, sl.adjustedon)) =
(select min(abs(DATEDIFF(ss, pnd_date, sl.adjustedon)))
from PINVDET
where pnd_invno = sl.invno))
from vwstocklog sl where sl.invno in (select invno from vwStockDiff)
order by sl.invno, sl.adjustedon
When I run the above query I get the error:
Multiple columns are specified in an aggregated expression containing an outer reference. If an expression being aggregated contains an outer reference, then that outer reference must be the only column referenced in the expression.
I understand it's saying that the expression min(abs(DATEDIFF(ss, pnd_date, sl.adjustedon))) is the problem because it references sl.adjusted on in the min() aggregate, and it cannot do so unless it's the only column referenced in the aggregate expression. What I'm not sure about is how to go about fixing it.
What I'm attempting to do here is find the pnd_invoiceno value on the record in pinvdet with the pnd_date value nearest to sl.adjustedon for the same item (and I recognize that this has the possibility of linking to multiple records).
Any ideas on how I might adjust this query to accomplish that?
Second attempt (filter first):
With x as (
select
sl.invno,
sl.adjustedon,
p.pnd_invoiceno,
rank() over (
partition by sl.invno
order by abs(datediff(ss, p.pnd_date, sl.adjustedon))
) rk
from
vwstocklog sl
inner join
pinvdet p
on p.pnd_invno = sl.invno
Where
Exists (
Select
'x'
From
vwStockDiff sd
Where
sl.invno = sd.invno
)
)
Select
x.invno,
x.adjustedon,
x.pnd_invoiceno
From
x
Where
x.rk = 1
order by
x.invno,
x.adjustedon
First attempt:
With x as (
select
sl.invno,
sl.adjustedon,
p.pnd_invoiceno,
rank() over (
partition by sl.invno
order by abs(datediff(ss, p.pnd_date, sl.adjustedon))
) rk
from
vwStockDiff sd
inner join
vwstocklog sl
on sl.invno = sd.invno
inner join
pinvdet p
on p.pnd_invno = sl.invno
)
Select
x.invno,
x.adjustedon,
x.pnd_invoiceno
From
x
Where
x.rk = 1
order by
x.invno,
x.adjustedon
If you happen to have two times that are an equal distance away, this will return a row for both. Replace rank() with row_number() if you'd only like 1.
SQLFiddle doesn't seem to be working at the moment, so I can't test this. There's probably syntax errors.

Limit join to one row

I have the following query:
SELECT sum((select count(*) as itemCount) * "SalesOrderItems"."price") as amount, 'rma' as
"creditType", "Clients"."company" as "client", "Clients".id as "ClientId", "Rmas".*
FROM "Rmas" JOIN "EsnsRmas" on("EsnsRmas"."RmaId" = "Rmas"."id")
JOIN "Esns" on ("Esns".id = "EsnsRmas"."EsnId")
JOIN "EsnsSalesOrderItems" on("EsnsSalesOrderItems"."EsnId" = "Esns"."id" )
JOIN "SalesOrderItems" on("SalesOrderItems"."id" = "EsnsSalesOrderItems"."SalesOrderItemId")
JOIN "Clients" on("Clients"."id" = "Rmas"."ClientId" )
WHERE "Rmas"."credited"=false AND "Rmas"."verifyStatus" IS NOT null
GROUP BY "Clients".id, "Rmas".id;
The problem is that the table "EsnsSalesOrderItems" can have the same EsnId in different entries. I want to restrict the query to only pull the last entry in "EsnsSalesOrderItems" that has the same "EsnId".
By "last" entry I mean the following:
The one that appears last in the table "EsnsSalesOrderItems". So for example if "EsnsSalesOrderItems" has two entries with "EsnId" = 6 and "createdAt" = '2012-06-19' and '2012-07-19' respectively it should only give me the entry from '2012-07-19'.
SELECT (count(*) * sum(s."price")) AS amount
, 'rma' AS "creditType"
, c."company" AS "client"
, c.id AS "ClientId"
, r.*
FROM "Rmas" r
JOIN "EsnsRmas" er ON er."RmaId" = r."id"
JOIN "Esns" e ON e.id = er."EsnId"
JOIN (
SELECT DISTINCT ON ("EsnId") *
FROM "EsnsSalesOrderItems"
ORDER BY "EsnId", "createdAt" DESC
) es ON es."EsnId" = e."id"
JOIN "SalesOrderItems" s ON s."id" = es."SalesOrderItemId"
JOIN "Clients" c ON c."id" = r."ClientId"
WHERE r."credited" = FALSE
AND r."verifyStatus" IS NOT NULL
GROUP BY c.id, r.id;
Your query in the question has an illegal aggregate over another aggregate:
sum((select count(*) as itemCount) * "SalesOrderItems"."price") as amount
Simplified and converted to legal syntax:
(count(*) * sum(s."price")) AS amount
But do you really want to multiply with the count per group?
I retrieve the the single row per group in "EsnsSalesOrderItems" with DISTINCT ON. Detailed explanation:
Select first row in each GROUP BY group?
I also added table aliases and formatting to make the query easier to parse for human eyes. If you could avoid camel case you could get rid of all the double quotes clouding the view.
Something like:
join (
select "EsnId",
row_number() over (partition by "EsnId" order by "createdAt" desc) as rn
from "EsnsSalesOrderItems"
) t ON t."EsnId" = "Esns"."id" and rn = 1
this will select the latest "EsnId" from "EsnsSalesOrderItems" based on the column creation_date. As you didn't post the structure of your tables, I had to "invent" a column name. You can use any column that allows you to define an order on the rows that suits you.
But remember the concept of the "last row" is only valid if you specifiy an order or the rows. A table as such is not ordered, nor is the result of a query unless you specify an order by
Necromancing because the answers are outdated.
Take advantage of the LATERAL keyword introduced in PG 9.3
left | right | inner JOIN LATERAL
I'll explain with an example:
Assuming you have a table "Contacts".
Now contacts have organisational units.
They can have one OU at a point in time, but N OUs at N points in time.
Now, if you have to query contacts and OU in a time period (not a reporting date, but a date range), you could N-fold increase the record count if you just did a left join.
So, to display the OU, you need to just join the first OU for each contact (where what shall be first is an arbitrary criterion - when taking the last value, for example, that is just another way of saying the first value when sorted by descending date order).
In SQL-server, you would use cross-apply (or rather OUTER APPLY since we need a left join), which will invoke a table-valued function on each row it has to join.
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
-- CROSS APPLY -- = INNER JOIN
OUTER APPLY -- = LEFT JOIN
(
SELECT TOP 1
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(#in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(#in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
) AS FirstOE
In PostgreSQL, starting from version 9.3, you can do that, too - just use the LATERAL keyword to achieve the same:
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
LEFT JOIN LATERAL
(
SELECT
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
LIMIT 1
) AS FirstOE
Try using a subquery in your ON clause. An abstract example:
SELECT
*
FROM table1
JOIN table2 ON table2.id = (
SELECT id FROM table2 WHERE table2.table1_id = table1.id LIMIT 1
)
WHERE
...