SQL multi-table query guidance - sql

I have the following query:
SELECT
_RES_COLL_EVM00012.MachineID,
_RES_COLL_EVM00012.Name,
v_GS_NETWORK_ADAPTER_CONFIGUR.IPAddress0,
v_GS_NETWORK_ADAPTER_CONFIGUR.DefaultIPGateway0,
v_GS_NETWORK_ADAPTER_CONFIGUR.TimeStamp,
v_GS_NETWORK_ADAPTER_CONFIGUR.RevisionID
FROM
_RES_COLL_EVM00012
LEFT JOIN v_GS_NETWORK_ADAPTER_CONFIGUR
ON _RES_COLL_EVM00012.MachineID = v_GS_NETWORK_ADAPTER_CONFIGUR.ResourceID
WHERE
v_GS_NETWORK_ADAPTER_CONFIGUR.IPEnabled0 = 1
AND v_GS_NETWORK_ADAPTER_CONFIGUR.IPAddress0 != '0.0.0.0'
AND v_GS_NETWORK_ADAPTER_CONFIGUR.IPAddress0 IS NOT NULL
AND v_GS_NETWORK_ADAPTER_CONFIGUR.DefaultIPGateway0 != '0.0.0.0'
AND v_GS_NETWORK_ADAPTER_CONFIGUR.DefaultIPGateway0 IS NOT NULL
ORDER BY
_RES_COLL_EVM00012.Name ASC,
v_GS_NETWORK_ADAPTER_CONFIGUR.TimeStamp DESC,
v_GS_NETWORK_ADAPTER_CONFIGUR.RevisionID DESC
Which returns something like the following:
MachineID Name IPAddress0 DefaultGatewayIP0 TimeStamp RevisionID
16777323 CTNB21 192.168.17.134 192.168.17.254 9/09/2013 13:07:11 8
16777323 CTNB21 192.168.17.143 192.168.17.254 9/09/2013 13:07:11 6
16777585 CTNB26 192.168.16.106 192.168.16.254 28/10/2013 22:39:55 33
16777585 CTNB26 192.168.16.116 192.168.16.254 28/10/2013 22:39:55 27
Obviously ResourceID is not unique in the table v_GS_NETWORK_ADAPTER_CONFIGUR. What I need to do is display every row from the table _RES_COLL_EVM00012 along with a SINGLE row for each from v_GS_NETWORK_ADAPTER_CONFIGUR.
The row selected from v_GS_NETWORK_ADAPTER_CONFIGUR should be the one with the most recent TimeStamp and the greatest RevisionID.
Note also I do not actually want to select MachineID, TimeStamp or RevisionID, I have just done so to help better explain my request.
One more thing, if a row does not exist in v_GS_NETWORK_ADAPTER_CONFIGUR with a match for the MachineID/ResourceID, I still need to output the Name but with blank values for IPAddress0 and DefaultGatewayIP0
So to clarify I would like the example result set to look like this instead:
Name IPAddress0 DefaultGatewayIP0
CTNB21 192.168.17.134 192.168.17.254
CTNB26 192.168.16.106 192.168.16.254

Try this:
SELECT
--_RES_COLL_EVM00012.MachineID,
_RES_COLL_EVM00012.Name,
ISNULL(v_GS_NETWORK_ADAPTER_CONFIGUR.IPAddress0,'') as IPAddress0,
ISNULL(v_GS_NETWORK_ADAPTER_CONFIGUR.DefaultIPGateway0,'') as DefaultIPGateway0
--v_GS_NETWORK_ADAPTER_CONFIGUR.TimeStamp,
--v_GS_NETWORK_ADAPTER_CONFIGUR.RevisionID
FROM
_RES_COLL_EVM00012
LEFT JOIN v_GS_NETWORK_ADAPTER_CONFIGUR
ON _RES_COLL_EVM00012.MachineID = v_GS_NETWORK_ADAPTER_CONFIGUR.ResourceID
LEFT JOIN (SELECT a.ResourceID,a.RevisionID, MAX(a.TimeStamp) as TimeStamp
FROM v_GS_NETWORK_ADAPTER_CONFIGUR a
join (SELECT ResourceID, MAX(RevisionID) as RevisionID
FROM v_GS_NETWORK_ADAPTER_CONFIGUR
GROUP BY ResourceID) b
ON a.ResourceID=b.ResourceID
GROUP BY a.ResourceID,a.RevisionID
)c
ON v_GS_NETWORK_ADAPTER_CONFIGUR.ResourceID=c.ResourceID
AND v_GS_NETWORK_ADAPTER_CONFIGUR.RevisionID=c.RevisionID
AND v_GS_NETWORK_ADAPTER_CONFIGUR.TimeStamp=c.TimeStamp
WHERE
c.RevisionID IS NOT NULL
ORDER BY
_RES_COLL_EVM00012.Name ASC,
v_GS_NETWORK_ADAPTER_CONFIGUR.TimeStamp DESC,
v_GS_NETWORK_ADAPTER_CONFIGUR.RevisionID DESC

Use DENSE_RANK()OVER(PARTITION BY RevisionID,TimeStamp ORDER BY RevisionID,TimeStamp DESC) in select statement as below.
SELECT *
FROM (SELECT _RES_COLL_EVM00012.MachineID,
_RES_COLL_EVM00012.Name,
v_GS_NETWORK_ADAPTER_CONFIGUR.IPAddress0,
v_GS_NETWORK_ADAPTER_CONFIGUR.DefaultIPGateway0,
v_GS_NETWORK_ADAPTER_CONFIGUR.TimeStamp,
v_GS_NETWORK_ADAPTER_CONFIGUR.RevisionID,
DENSE_RANK() OVER (PARTITION BY RevisionID, TimeStamp
ORDER BY RevisionID, TimeStamp DESC) RowID
FROM _RES_COLL_EVM00012
LEFT JOIN v_GS_NETWORK_ADAPTER_CONFIGUR
ON _RES_COLL_EVM00012.MachineID = v_GS_NETWORK_ADAPTER_CONFIGUR.ResourceID
WHERE v_GS_NETWORK_ADAPTER_CONFIGUR.IPEnabled0 = 1
AND v_GS_NETWORK_ADAPTER_CONFIGUR.IPAddress0 != '0.0.0.0'
AND v_GS_NETWORK_ADAPTER_CONFIGUR.IPAddress0 IS NOT NULL
AND v_GS_NETWORK_ADAPTER_CONFIGUR.DefaultIPGateway0 != '0.0.0.0'
AND v_GS_NETWORK_ADAPTER_CONFIGUR.DefaultIPGateway0 IS NOT NULL
) XYZ
WHERE XYZ.RowID = 1
For more articles on SQL Server please visit SQL Server Basics

Related

SQL query to return duplicate rows for certain column, but with unique values for another column

I have written the query shown here that combines three tables and returns rows where the at_ticket_num from appeal_tickets is duplicated but against a different at_sys_ref value
select top 100
t.t_reference, at.at_system_ref, at_ticket_num, a.a_case_ref
from
tickets t, appeal_tickets at, appeals_2 a
where
t.t_reference in ('AB123','AB234') -- filtering on these values so that I can see that its working
and t.t_number = at.at_ticket_num
and at.at_system_ref = a.a_system_ref
and at.at_ticket_num IN (select at_ticket_num
from appeal_tickets
group by at_ticket_num
having count(distinct at_system_ref) > 1)
order by
t.t_reference desc
This is the output:
t_reference at_system_ref at_ticket_num a_case_ref
-------------------------------------------------------
AB123 30838974 23641583 1111979010
AB123 30838976 23641583 1111979010
AB234 30839149 23641520 1111977352
AB234 30839209 23641520 1111988003
I want to modify this so that it only returns records where t_reference is duplicated but against a different a_case_ref. So in above case only records for AB234 would be returned.
Any help would be much appreciated.
You want all ticket appeals that have more than one system reference and more than one case reference it seems. You can join the tables, count the occurrences per ticket and then only keep the tickets that match these criteria.
select *
from
(
select
t.t_reference, at.at_system_ref, at.at_ticket_num, a.a_case_ref,
count(distinct a.a_system_ref) over (partition by at.at_ticket_num) as sysrefs,
count(distinct a.a_case_ref) over (partition by at.at_ticket_num) as caserefs
from tickets t
join appeal_tickets at on at.at_ticket_num = t.t_number
join appeals_2 a on a.a_system_ref = at.at_system_ref
) counted
where sysrefs > 1 and caserefs > 1
order by t.t_reference, at.at_system_ref, at.at_ticket_num, a.a_case_ref;
Correction
It seems that SQL Server still doesn't support COUNT(DISTINCT ...) OVER (...). You can count distinct values in a subquery though. Replace
count(distinct a.a_system_ref) over (partition by at.at_ticket_num) as sysrefs,
by
(
select count(distinct a2.a_system_ref)
from appeal_tickets at2
join appeals_2 a2 on a2.a_system_ref = at2.at_system_ref
where at2.at_ticket_num = t.t_number
) as sysrefs,
An alternative workaround is to use DENSE_RANK in two directions (found here: https://stackoverflow.com/a/53518204/2270762):
dense_rank() over (partition by at.at_ticket_num order by a.a_system_ref) +
dense_rank() over (partition by at.at_ticket_num order by a.a_system_ref desc) -
1 as sysrefs,
with data as (
<your query plus one column>,
case when
min() over (partition by t.t_reference)
<>
max() over (partition by t.t_reference)
then 1 end as dup
)
select * from data where dup = 1

Multiple results - Need only the latest price

I need to find the latest price for some items
This is my query:
SELECT
MAX("POPORH1"."DATE") as "PO DATE",
"ICSHEH"."DOCNUM",
"ICSHEH"."TRANSDATE",
"ICSHEH"."FISCYEAR",
"ICSHEH"."FISCPERIOD",
"ICSHEH"."REFERENCE",
"ICSHED"."ITEMNO",
"ICSHED"."ITEMDESC",
"ICSHED"."LOCATION",
"ICSHED"."QUANTITY",
"ICSHED"."UNIT",
"POPORL"."UNITCOST"
FROM (("CABDAT"."dbo"."ICSHEH" "ICSHEH"
INNER JOIN
"CABDAT"."dbo"."ICSHED" "ICSHED" ON "ICSHEH"."SEQUENCENO"="ICSHED"."SEQUENCENO")
INNER JOIN "CABDAT"."dbo"."POPORL" "POPORL" ON "ICSHED"."ITEMNO"="POPORL"."ITEMNO")
INNER JOIN "CABDAT"."dbo"."POPORH1" "POPORH1" ON "POPORL"."PORHSEQ"="POPORH1"."PORHSEQ"
WHERE "ICSHED"."SEQUENCENO"=55873
group by
"ICSHEH"."DOCNUM",
"ICSHEH"."TRANSDATE",
"ICSHEH"."FISCYEAR",
"ICSHEH"."FISCPERIOD",
"ICSHEH"."REFERENCE",
"ICSHED"."ITEMNO",
"ICSHED"."ITEMDESC",
"ICSHED"."LOCATION",
"ICSHED"."QUANTITY",
"ICSHED"."UNIT",
"POPORL"."UNITCOST"
This query returns multiple results
These are the results:
"PODATE"='20180405' "ITEMNO"='2944' "UNITCOST"='0.266750'
"PODATE"='20180405' "ITEMNO"='2946' "UNITCOST"='0.266750'
"PODATE"='20170208' "ITEMNO"='2944' "UNITCOST"='0.250780'
"PODATE"='20170208' "ITEMNO"='2944' "UNITCOST"='0.250780'
"PODATE"='20170208' "ITEMNO"='2946' "UNITCOST"='0.250780'
"PODATE"='20170208' "ITEMNO"='2946' "UNITCOST"='0.250780'
I need to have only
"PODATE"='20180405' "ITEMNO"='2944' "UNITCOST"='0.266750'
"PODATE"='20180405' "ITEMNO"='2946' "UNITCOST"='0.266750'
I am learning SQL, so please be patient with my ignorance...
Thanks a lot!
You just need row_number().
WITH cte as (
SELECT *, ROW_NUMBER() OVER (PARTITION BY "ITEMNO" ORDER BY "PODATE" DESC) as rn
FROM "ICSHED" -- or join tables
WHERE "ICSHED"."SEQUENCENO"=55873
)
SELECT *
FROM cte where rn = 1
Or if you only need the highest value without any grouping can use TOP 1
SELECT TOP 1 *
FROM "ICSHED" -- or join tables
WHERE "ICSHED"."SEQUENCENO"=55873
ORDER "PODATE" DESC
By my understanding, you want top 2 rows with recent date. so I try this,
select top 2 * from yourtable order by dateCol desc

Group by not working to get count of a column with other max record in sql

I have a table named PublishedData, see image below
I'm trying to get the output like, below image
I think you can use a query like this:
SELECT dt.DistrictName, ISNULL(dt.Content, 'N/A') Content, dt.UpdatedDate, mt.LastPublished, mt.Unpublished
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY DistrictName ORDER BY UpdatedDate DESC, ISNULL(Content, 'zzzzz')) seq
FROM PublishedData) dt
INNER JOIN (
SELECT DistrictName, MAX(LastPublished) LastPublished, COUNT(CASE WHEN IsPublished = 0 THEN 1 END) Unpublished
FROM PublishedData
GROUP BY DistrictName) mt
ON dt.DistrictName = mt.DistrictName
WHERE
dt.seq = 1;
Because I think you use an order over UpdatedDate, Content to gain you two first columns.
Check out something like this (I don't have your tables, but you will get the idea where to follow with your query):
SELECT DirectName,
MAX(UpdatedDate),
MAX(LastPublished),
(
SELECT COUNT(*)
FROM PublishedData inr
WHERE inr.DirectName = outr.DirectName
AND inr.IsPublished = 0
) AS Unpublished
FROM PublishedData outr
GROUP BY DirectName
We should required a unique identity for that required output in PublishedData Table,Because We can't get the Latest content from given Schema.
If you want data apart from content like DistictName,updatedDate,LastPublishedDate and count of Unpublished records ,Please use Query given below :
select T1.DistrictName,T1.UpdatedDate,T1.LastPublished,T2.Unpublished from
(select DistrictName,Max(UpdateDate) as UpdatedDate,Max(LastPublished) as LastPublished from PublishedData group by DistrictName) T1
inner join
(select DistrictName,count(IsPublished) as Unpublished from PublishedData where isPublished=0 group by DistrictName) T2 ON T1.DistrictName=T2.DistrictName ORDER BY T2.Unpublished DESC

How do I refer to a record in sql that immediately precedes another record in a group?

I have a weird update query to write.
Here's the table
PK-ID (int) --- FK-ID (int) --- Value (int)
In my data set, if I group by FK-ID and order by PK-ID, suppose this is an example of one group:
5 --- 10 --- 23
7 --- 10 --- 49
8 --- 10 --- 81
Due to a bug in some old software, records 7 and 8 have incorrect values. The correct value for 7 is (49-23) = 26 and the correct value for 8 is (81-49) = 32. Record 5 is correct.
I need to update each record to subtract the value of the record immediately preceding it when it is grouped by FK-ID and ordered by PK-ID. If there is no preceding record I do not need to change the value.
Is there a way to write a general sql update query to accomplish this? How would I (conditionally) retrieve the value of the preceding record in the group? I'm using SQL server 2008.
Thanks!
with ordered as (
select *, rn = row_number() over (partition by fk_id order by pk_id)
from tbl
)
update cur
set value = cur.value - prior.value
from ordered cur
join ordered prior on prior.fk_id = cur.fk_id
and prior.rn = cur.rn-1;
This is what I believe to be the correct answer, using a similar idea to the previous one. The toupdate subquery calculates the values, based on the rules in the question (update records with the same foreign key and consecutive primary keys). It does assume that the ids are nuemric values of some sort.
with toupdate as (
select t.pkid, t.value - tprev.value as newval
from t join
t tprev
on t.pkid = tprev.pkid+1 and t.fkid = tprev.fkid
)
update t
set value = newvalue
from toupdate
where t.pkid = toupdate.pkid
update t set value = value -
isnull((select top 1 value
from t t2
where t2.FKID=t.FKID
and t2.PKID<t.PKID
order by PKID desc),0);
Here is a SQLFiddle demo
I hope it should return what you want(sorry, I cannot try it the moment); you just need to incorporate it with UPDATE
WITH cte1 AS
(SELECT pk_id, fk_id, value, ROW_NUMBER() OVER (PARTITION BY fk_id ORDER BY pk_id DESC)
as num
FROM your_table
)
SELECT a.*,
--CASE
-- WHEN b.pk_id IS NOT NULL THEN a.value-b.value
-- ELSE 0 END
a.value-b.value as valid_number
FROM cte1 a
--LEFT JOIN cte1 b ON (b.fk_id = a.fk_id AND b.num = a.num-1)
INNER JOIN cte1 b ON (b.fk_id = a.fk_id AND b.num = a.num-1)

Variant use of the GROUP BY clause in TSQL

Imagine the following schema and sample data (SQL Server 2008):
OriginatingObject
----------------------------------------------
ID
1
2
3
ValueSet
----------------------------------------------
ID OriginatingObjectID DateStamp
1 1 2009-05-21 10:41:43
2 1 2009-05-22 12:11:51
3 1 2009-05-22 12:13:25
4 2 2009-05-21 10:42:40
5 2 2009-05-20 02:21:34
6 1 2009-05-21 23:41:43
7 3 2009-05-26 14:56:01
Value
----------------------------------------------
ID ValueSetID Value
1 1 28
etc (a set of rows for each related ValueSet)
I need to obtain the ID of the most recent ValueSet record for each OriginatingObject. Do not assume that the higher the ID of a record, the more recent it is.
I am not sure how to use GROUP BY properly in order to make sure the set of results grouped together to form each aggregate row includes the ID of the row with the highest DateStamp value for that grouping. Do I need to use a subquery or is there a better way?
You can do it with a correlated subquery or using IN with multiple columns and a GROUP-BY.
Please note, simple GROUP-BY can only bring you to the list of OriginatingIDs and Timestamps. In order to pull the relevant ValueSet IDs, the cleanest solution is use a subquery.
Multiple-column IN with GROUP-BY (probably faster):
SELECT O.ID, V.ID
FROM Originating AS O, ValueSet AS V
WHERE O.ID = V.OriginatingID
AND
(V.OriginatingID, V.DateStamp) IN
(
SELECT OriginatingID, Max(DateStamp)
FROM ValueSet
GROUP BY OriginatingID
)
Correlated Subquery:
SELECT O.ID, V.ID
FROM Originating AS O, ValueSet AS V
WHERE O.ID = V.OriginatingID
AND
V.DateStamp =
(
SELECT Max(DateStamp)
FROM ValueSet V2
WHERE V2.OriginatingID = O.ID
)
SELECT OriginatingObjectID, id
FROM (
SELECT id, OriginatingObjectID, RANK() OVER(PARTITION BY OriginatingObjectID
ORDER BY DateStamp DESC) as ranking
FROM ValueSet)
WHERE ranking = 1;
This can be done with a correlated sub-query. No GROUP-BY necessary.
SELECT
vs.ID,
vs.OriginatingObjectID,
vs.DateStamp,
v.Value
FROM
ValueSet vs
INNER JOIN Value v ON v.ValueSetID = vs.ID
WHERE
NOT EXISTS (
SELECT 1
FROM ValueSet
WHERE OriginatingObjectID = vs.OriginatingObjectID
AND DateStamp > vs.DateStamp
)
This works only if there can not be two equal DateStamps for a OriginatingObjectID in the ValueSet table.