Combining data into a single cell while summing a seperate cell - sql

I don't know how to word my question very well, so I will start with the data being returned:
prop_id | assessed_value | sale_id
35004 | 401200 | 1920831
35005 | 40500 | 1920831
35023 | 11300 | 1920831
34380 | 139100 | 1915846
127959 | 286400 | 1915882
I would like it to return:
prop_id | assessed_value | sale_id
35004, 35005, 35023 | 453000(Sum of the 3 parcels) | 1920831
34380 | 139100 | 1915846
127959 | 286400 | 1915882
So my main goal is to combine all the parcels on the sale_id field into a string, but only when a sale id has multiple prop_id, and then sum up the assessed value of all those. This is the query I am using to get the first set of data...
select
pv1.[prop_id],
pv1.[assessed_val],
ld1.[sale_id]
from dbo.land_detail as ld1
join dbo.property_val as pv1 on
pv1.[prop_id] = ld1.[prop_id] and
pv1.[prop_val_yr] = ld1.[prop_val_yr] and
pv1.[sup_num] = ld1.[sup_num]
left join dbo.sale as sale1 on
sale1.[chg_of_owner_id] = ld1.[sale_id]
where
pv1.[prop_inactive_dt] is null
order by sale_id, prop_id
prop_id is of data type INT
assessed_val is of data type numeric(14,0)
sale_id is of data type INT

You can use WITH to create a temporary table then you can use STUFF() FOR XML PATH to concatenate the prop_id field into one row
WITH temp AS (
select pv1.[prop_id],
pv1.[assessed_val],
ld1.[sale_id],
sale1.[sl_price],
sale1.[sl_dt],
(pv1.[assessed_val]/NULLIF(sale1.[sl_price],0) as Ratio
from dbo.land_detail as ld1 with(nolock)
join dbo.property_val as pv1 with(nolock)
on pv1.[prop_id] = ld1.[prop_id]
and pv1.[prop_val_yr] = ld1.[prop_val_yr]
and pv1.[sup_num] = ld1.[sup_num]
left join dbo.sale as sale1 with(nolock)
on sale1.[chg_of_owner_id] = ld1.[sale_id]
where sale1.sl_dt <= '04/30/16'
and sale1.sl_dt >= '05/01/15'
and pv1.[sub_type] = 'r'
and pv1.[prop_val_yr] = 2016
and pv1.[prop_inactive_dt] is null
order by sale_id, prop_id
)
SELECT STUFF(( SELECT ', ' + CAST(prop_id AS VARCHAR)
FROM temp
WHERE sale_id = t.sale_id
FOR XML PATH(''),TYPE)
.value('.','NVARCHAR(MAX)'),1,2,'') AS parcels,
SUM(t.assessed_val) assessed_val,
t.sale_id,
t.sl_price,
t.sl_dt,
SUM(t.ratio) ratio
FROM temp t
GROUP BY t.sale_id,
t.sl_price,
t.sl_dt
ORDER BY t.sale_id DESC

Put everything into a temp table (#temp) as below. Then make a self join to select the values. There are several ways of doing concatenation using (SUFF(), XML PATH, etc). Search on the internet on how to concatenate rows into comma separated string refer to this link for example
select
pv1.[prop_id],
pv1.[assessed_val],
ld1.[sale_id],
sale1.[sl_price],
sale1.[sl_dt],
(pv1.[assessed_val]/sale1.[sl_price]) as Ratio
--------Temp Table------------
INTO #temp
------------------------------
from dbo.land_detail as ld1 with(nolock)
join dbo.property_val as pv1 with(nolock) on
pv1.[prop_id] = ld1.[prop_id] and
pv1.[prop_val_yr] = ld1.[prop_val_yr] and
pv1.[sup_num] = ld1.[sup_num]
left join dbo.sale as sale1 with(nolock) on
sale1.[chg_of_owner_id] = ld1.[sale_id]
where
sale1.sl_dt <= '04/30/16' and
sale1.sl_dt >= '05/01/15' and
pv1.[sub_type] = 'r' and
pv1.[prop_val_yr] = 2016 and
pv1.[prop_inactive_dt] is null
order by sale_id, prop_id

Related

ORA-00904: "Q"."ITEM_NO": invalid identifier - Oracle SQL group by issue

I have this query to fetch item no's and their quantities filtered by a specific unit.
select q.item_no, sum(q.quantity) from item_unit_quantity q
where q.unit_no = 'PH00000096' group by q.item_no
Now I want to add price column which I get from another two tables
There is a condition, Price is always the maximum of the latest three GRN dates.
select max(price) from (select price from grn_item gi join grn g on gi.grn_no = g.grn_no
where gi.item_no = 'IT00001896' order by g.grn_date desc) where rownum <= 3
When I combined the queries as below,
select q.item_no, sum(q.quantity),
(select max(price) from (select price from grn_item gi join grn g on gi.grn_no = g.grn_no
where gi.item_no = q.item_no order by g.grn_date desc) where rownum <= 3) "PRICE"
from item_unit_quantity q where q.unit_no = 'PH00000096' group by q.item_no
I get
ORA-00904: "Q"."ITEM_NO": invalid identifier
But if I change the above as "gi.item_no = 'IT00001896'" I get
ITEM_NO | Sum(Quantity) | Price
--------------------------------------
IT00012824 | 14 | 3.68
IT00006345 | 494 | 3.68
IT00001896 | 5 | 3.68
I still want to display the Item no, Item quantity and price for each Item no in this query, how can I do this?
Your question is a bit hard to follow without sample data. However, subqueries with GROUP BY can be finicky. A simple solution is to move the subquery to the FROM clause:
select q.item_no, sum(q.quantity), max(max_price)
from item_unit_quantity q left join
(select gi.item_no, max(g.price) as max_price
from grn_item gi join
grn g
on gi.grn_no = g.grn_no
group by gi.item_no
) gi
on gi.item_no = q.item_no
where q.unit_no = 'PH00000096'
group by q.item_no

Group By Dynamic Ranges in SQL (cockroachdb/postgres)

I have a query that looks like
select s.session_id, array_agg(sp.value::int8 order by sp.value::int8) as timestamps
from sessions s join session_properties sp on sp.session_id = s.session_id
where s.user_id = '6f129b1c-43a6-4871-86f6-1749bfe1a5af' and sp.key in ('SleepTime', 'WakeupTime') and value != 'None' and value::int8 > 0
group by s.session_id
The result would look like
f321c813-7927-47aa-88c3-b3250af34afa | {1588499070,1588504354}
f38a8841-c402-433d-939d-194eca993bb6 | {1588187599,1588212803}
2befefaf-3b31-46c9-8416-263fa7b9309d | {1589912247,1589935771}
3da64787-65cd-4305-b1ac-1393e2fb11a9 | {1589741569,1589768453}
537e69aa-c39d-484d-9108-2f2cd956d4ee | {1588100398,1588129026}
5a9470ff-f930-491f-a57d-8c089e535d53 | {1589140368,1589165092}
The first column is a unique id and the second column is from and to timestamps.
Now I have a third table which has some timeseries data
records
------------------------
timestamp | name | value
Is it possible to find avg(value) from from records in group of session_ids over the from and to timestamps.
I could run a for loop in the application and do a union to get the desired result. But I was wondering if that is possible in postgres or cockroachdb
I wouldn't aggregate the two values but use two joins to find them. That way you can be sure which value belongs to which property.
Once you have that, you can join that result to your records table.
with ranges as (
select s.session_id, st.value as from_value, wt.value as to_value
from sessions s
join session_properties st on sp.session_id = s.session_id and st.key = 'SleepTime'
join session_properties wt on wt.session_id = s.session_id and wt.key = 'WakeupTime'
where s.user_id = '6f129b1c-43a6-4871-86f6-1749bfe1a5af'
and st.value != 'None' and wt.value::int8 > 0
and wt.value != 'None' and wt.value::int8 > 0
)
select ra.session_id, avg(rc.value)
from records rc
join ranges ra
on ra.from_value >= rc.timewstamp
and rc.timestamp < ra.to_value
group by ra.session_id;

Can I concat ONLY one column within a larger SQL query OR transpose multiple rows into extra columns?

This is for a vendor web application that is trying to generate information to put into an email and send it.
I've seen similar answers that use FOR XML PATH but that concats the whole return into a single line where I need a single line per item. I've also seen answers that use subqueries but that doesn't seem to work in this instance.
I have 2 tables; OrderItem OI that lists all items ever ordered, OrderItemProperties OIP that lists all the options any customer has made for all items (about 5 to 6 for every item). None of the properties are identified by property type in any way, the table just lists [ItemID, PropUID, PropValue] where PropUID are just sequential numbers.
So far what I have is:
SELECT OI.ItemID, OI.TotalPrice, OIP.PropValue
FROM OrderItemProperties OIP
JOIN OrderItem OI ON OI.ItemID = OIP.ItemID
WHERE OI.OrderID = 12345
AND (OIP.PropValue IN ('MSNISA', 'KHNISA') OR OIP.PropValue LIKE '<DateTime>%')
Unfortunately, the only parameter the software passes to the query is OI.OrderID. This should generate a row for each item the in customers order which is then passed to an xlst document using <xsl:for-each select..> but what (obviously!) I'm getting is
ItemID | TotalPrice | PropValue
555 | 9.99 | MSNISA
555 | 9.99 | <DateTime><Server>17/01/2020...
556 | 19.50 | KHNISA
556 | 19.50 | <DateTime><Server>18/01/2020...
What I'm trying to get to is:
ItemID | TotalPrice | PropValue
555 | 9.99 | MSNISA, <DateTime><Server>17/01/2020...
556 | 19.50 | KHNISA, <DateTime><Server>18/01/2020...
Or what would be even better:
ItemID | TotalPrice | PropValue[1] | PropValue[2]
555 | 9.99 | MSNISA | <DateTime><Server>17/01/2020...
556 | 19.50 | KHNISA | <DateTime><Server>18/01/2020...
The application I'm using only has a limited number of parameters that it passes to a query but it does allow me to create and declare variables within a query.
Any help is appreciated as I'm just piecing things together as I go at the moment!
I think aggregation does what you want:
SELECT OI.ItemID, OI.TotalPrice,
MIN(OIP.PropValue), MAX(OIP.PropValue)
FROM OrderItemProperties OIP JOIN
OrderItem OI
ON OI.ItemID = OIP.ItemID
WHERE OI.OrderID = 12345 AND
(OIP.PropValue IN ('MSNISA', 'KHNISA') OR OIP.PropValue LIKE '<DateTime>%')
GROUP BY OI.ItemID, OI.TotalPrice;
I believe that you could utilize the STUFF() command as you originally theorized. I don't have sample data to test this against, might be slight syntax issues. SQLFiddle.
-- Casts XML back to varchar
SELECT [oi].[itemid]
, MAX([oi].[totalprice]) AS "totalprice"
, STUFF(
(
SELECT ',' + [oip].[propvalue]
FROM [orderitemproperties] AS [oip]
WHERE [oip].[itemid] = [oi].[itemid] FOR XML PATH(''), TYPE
).value
( '.', 'varchar(max)' ), 1, 1, '') AS "propvalue"
FROM orderitem AS oi
GROUP BY [oi].[itemid];
-- ORIGINAL QUERY - experiences issues with xml to varchar formatting
SELECT [oi].[itemid]
, MAX([oi].[totalprice]) as "TotalPrice"
, STUFF(
(
SELECT ',' + [oip].[propvalue]
FROM [orderitemproperties] AS [oip]
WHERE [oip].[itemid] = [oi].[itemid] FOR XML PATH('')
), 1, 1, '') as "PropValue"
FROM orderitem AS oi
GROUP BY [oi].[itemid];
I think this will work but it's ugly. Ideally you would have some sort of Property Key that would describe what the "MSNISA" vs "" values. I could provide a little better answer if you can post the schema of your OIP table.
SELECT [ItemID] = OI.ItemID, [TotalPrice] = OI.TotalPrice, [PropValue1] = OIP1.PropValue, [PropValue2] = OIP2.PropValue
FROM OrderItem OI
JOIN OrderItemProperties OIP1 on OIP1.ItemID = OI.ItemID AND OIP1.PropValue = 'MSNISA'
JOIN OrderItemProperties OIP2 on OIP2.ItemID = OI.ItemID AND OIP2.Propvalue like '<DateTime>%'
WHERE OI.OrderID = 12345
UNION
SELECT [ItemID] = OI.ItemID, [TotalPrice] = OI.TotalPrice, [PropValue1] = OIP1.PropValue, [PropValue2] = OIP2.PropValue
FROM OrderItem OI
JOIN OrderItemProperties OIP1 on OIP1.ItemID = OI.ItemID AND OIP1.PropValue = 'KHNISA'
JOIN OrderItemProperties OIP2 on OIP2.ItemID = OI.ItemID AND OIP2.Propvalue like '<DateTime>%'
WHERE OI.OrderID = 12345

Query table with all names in one statement

I have a table that I cannot alter and I am trying to wrap my head around an appropriate way to query this table in one statement based on one given value.
Roughly here's what the table looks like:
What I'm after is a way to query the table using only the SubBody code in order to get all names associated with it.
for example,
<query> where SubBody = '1001'
returns
| HName | HSName | BName | BSName |
+-----------------------------------+
| Toys | Sport | Ball | Baseball |
SELECT Head.Name as HName ,
SubHead.Name as HSName ,
Body.Name as BName ,
SubBody.Name as BSName
FROM yourTable as SubBody
JOIN yourTable as Body
ON SubBody.Body = Body.Body
AND Body.SubBody IS NULL
JOIN yourTable as SubHead
ON Body.SubHead = SubHead.SubHead
AND SubHead.Body IS NULL
JOIN yourTable as Head
ON SubHead.Head = Head.Head
AND Head.SubHead IS NULL
WHERE SubBody.SubBody = '1001'
Although you can express the logic as joins, for some reason correlated subqueries come to mind first:
select (select th.name from t th where th.head = t.head and th.subhead is null) as hname,
(select ts.name from t ts where ts.head = t.head and ts.subhead = t.subhead and t.body is null) as sname,
(select tb.name from t tb where tb.head = t.head and tb.subhead = t.subhead and tb.body = t.body and tb.subbody is null) as bname,
t.name as bsname
from t
where t.subbody = 1001
This answer is close to what Juan Carlos has posted. Which one you like depends a bit on your style.
WITH BaseRecord AS(
SELECT *
FROM ToyTable
WHERE SubBody = '1001'
)
SELECT
h.Name AS HName,
hs.Name AS HSName,
b.Name AS BName,
bs.Name AS BSName
FROM BaseRecord bs
INNER JOIN ToyTable b ON bs.Body = b.Body AND ISNULL(b.SubBody,'') = ''
INNER JOIN ToyTable hs ON bs.SubHead = hs.SubHead AND ISNULL(hs.Body,'') = ''
INNER JOIN ToyTable h ON bs.Head = h.Head AND ISNULL(h.SubHead,'') = ''
I'm not sure whether your "empty" cells contain nulls or empty strings. Either way, both are taken into account here.

Combine results of two different records in one

I have an SQL data stored in a single table but with different type and I want to combine the result in one liner show in the example below. Anyone can give suggestion to this SQL query?
My SQL query to get this results:
SELECT NumValue, Label, Type
FROM (CustomPollerStatusTable
INNER JOIN CustomPollers ON (CustomPollerStatusTable.PollerID =
CustomPollers.CustomPollerID))
INNER JOIN Nodes ON (CustomPollerStatusTable.NodeID = Nodes.NodeID)
WHERE ((Nodes.Caption = 'fqdn') AND
(CustomPollers.Type = 'Student Info') AND
(CustomPollerStatusTable.NumValue > 89) AND
(NOT (CustomPollerStatusTable.Label LIKE '%.snapshot%')) AND
(NOT (CustomPollerStatusTable.Label LIKE '%aggr%')) AND
(NOT (CustomPollerStatusTable.Label = '/vol/scratch/'))
)
====================================
NumValue | Label | Type
====================================
90 | Student 1 | Student Info
10 | Student 1 | Student Class
====================================
The results that I would like to achieve:
==========================================================================
NumValue.Info | NumValue.Class | Label | Type.Info | Type.Class
==========================================================================
90 | 10 | Student 1 | Student Info | Student Class
==========================================================================
This should do it. Slightly different from nrathaus' answer. Obviously you can use as to change the column names to whatever you want.
select
s1.NumValue,
s2.NumValue,
s1.Label,
s1.Type,
s2.Type
from Student s1 inner join Student s2
on s1.Label = s2.Label
where s1.Type like '%Info'
and s2.Type like '%Class'
EDIT: Now that you have posted your select statement I think this might not work. Can you post the table structure?
EDIT2: This might work.
INSERT INTO TempTable(
NumValInfo,
Label,
TypeInfo)
SELECT
c.NumValue,
c.Label,
p.Type
FROM (CustomPollerStatusTable c INNER JOIN CustomPollers p
ON (c.PollerID = p.CustomPollerID))
INNER JOIN Nodes n
ON (c.NodeID = n.NodeID)
WHERE n.Caption = 'fqdn'
AND p.Type = 'Student Info'
AND c.NumValue > 89
AND NOT (c.Label LIKE '%.snapshot%')
AND NOT (c.Label LIKE '%aggr%')
AND NOT (c.Label = '/vol/scratch/')
AND p.Type like '%Info'
UPDATE TempTable set
NumValClass = c.NumValue,
TypeClass = p.Type
FROM (CustomPollerStatusTable c INNER JOIN CustomPollers p
ON (c.PollerID = p.CustomPollerID))
INNER JOIN Nodes n
ON (c.NodeID = n.NodeID)
INNER JOIN TempTable t
ON t.Label = c.Label
WHERE n.Caption = 'fqdn'
AND p.Type = 'Student Info'
AND c.NumValue > 89
AND NOT (c.Label LIKE '%.snapshot%')
AND NOT (c.Label LIKE '%aggr%')
AND NOT (c.Label = '/vol/scratch/')
AND p.Type like '%Class'
SELECT * FROM TempTable
I think this will do:
SELECT tblInfo.NumValue AS `NumValue.Info`,
tblClass.NumValue AS `NumValue.Class`,
Table.Label AS Label,
tblInfo.Type AS `Type.Info`,
tblClass.Type AS `Type.Class`
FROM (Table)
LEFT JOIN Table AS tblInfo ON tblInfo.Label = Table.Label ON tblInfo.Type = 'Student Info'
LEFT JOIN Table AS tblClass ON tblClass.Label = Table.Label ON tblClass.Type = 'Student Class'
GROUP BY Table.Label