Max value from joined table - sql

I have two tables:
Operations (op_id,super,name,last)
Orders (or_id,number)
Operations:
+--------------------------------+
|op_id| super| name | last|
+--------------------------------+
|1 1 OperationXX 1 |
|2 1 OperationXY 2 |
|3 1 OperationXC 4 |
|4 1 OperationXZ 3 |
|5 2 OperationXX 1 |
|6 3 OperationXY 2 |
|7 4 OperationXC 1 |
|8 4 OperationXZ 2 |
+--------------------------------+
Orders:
+--------------+
|or_id | number|
+--------------+
|1 2UY |
|2 23X |
|3 xx2 |
|4 121 |
+--------------+
I need query to get table:
+-------------------------------------+
|or_id |number |max(last)| name |
|1 2UY 4 OperationXC|
|2 23X 1 OperationXX|
|3 xx2 2 OperationXY|
|4 121 2 OperationXZ|
+-------------------------------------+

use corelared subquery and join
select o.*,a.last,a.name from
(
select super,name,last from Operations from operations t
where last = (select max(last) from operations t2 where t2.super=t.super)
) a join orders o on t1.super =o.or_id
you can use row_number as well
with cte as
(
select * from
(
select * , row_number() over(partition by super order by last desc) rn
from operations
) tt where rn=1
) select o.*,cte.last,cte.name from Orders o join cte on o.or_id=cte.super

SELECT Orders.or_id, Orders.number, Operations.name, Operations.last AS max
FROM Orders
INNER JOIN Operations on Operations.super = Orders.or_id
GROUP BY Orders.or_id, Orders.number, Operations.name;
I don't have a way of testing this right now, but I think this is it.
Also, you didn't specify the foreign key, so the join might be wrong.

Related

How to join two queries with count?

I have 3 tables in database:
Table 1: violation
| violation_id | violation_name |
|:-------------:|:--------------:|
|1 | No Parking |
|2 | Speed Contest |
|3 | No Helmet |
Table 2: violators
| violator_id | violation_id |
|:-------------:|:--------------:|
|1 |1 |
|2 |1 |
|3 |3 |
Table 2: previous_violator
| prev_violator_id| violation_id |
|:---------------:|:--------------:|
|1 |1 |
|2 |2 |
|3 |2 |
This view that I want:
| violation_name | Total |
|:-------------:|:--------------:|
|No Parking | 3 |
|Speed Contest | 2 |
|No Helmet | 1 |
I perform this code that joins the violator table and violation:
SELECT *,count(violators.violation_id) as vid
FROM violators
LEFT JOIN violation ON violation.violation_id = violators.violation_id
LEFT JOIN previous_violator ON previous_violator.violator_id = violators.violator_id
WHERE date_apphrehend BETWEEN '$from' AND '$to'
GROUP BY violators.violation_id
My problem is, I want to join the previous violator table that count to the total based on the violation_name.
You can first union all to get them into a single result and then count(*) it. Finally join with Violation to get names. ie:
select violation_name, count(*) as cnt
from (select violation_id from Violators
union all
select violation_id from previous_Violators) tmp
inner join Violation on tmp.violation_id = Violation.violation_id
group by Violation.violation_id, violation_name;
Sample DBFiddle demo.
PS: Sample is in postgreSQL but it would be the same for most backends. You didn't tag your backend.

How to disregard a row in a returned MS Access query when all fields bar one are distinct

I'm trying to create a query on Access 2010 which only produces a single row per patient. There are a really small number of patients (each patient represented by
a unique nhs_number in the table n) who are listed as having 2 practices in the table pp and so two rows are generated for them. Is there a way I can arbitrarily select one of the practices and ignore the other?
This is the query:
SELECT DISTINCT
n.nhs_number,
IIF(ch.care_home_date>#2/1/1900#, "TRUE", "FALSE") AS care_home,
pp.practice
FROM (nhs_no_tbl AS n
LEFT JOIN patient_practice_tbl AS pp ON n.nhs_number = pp.nhs_number)
LEFT JOIN patient_care_home_tbl AS ch ON n.nhs_number = ch.nhs_number;
The tables the query is using contains data along these lines:
nhs_no_tbl:
|nhs_number|
| -------- |
|1 |
|2 |
|3 |
|4 |
patient_practice_tbl:
|nhs_number|practice|
| -------- | ------ |
|1 |GP_A |
|2 |GP_A |
|3 |GP_B |
|4 |GP_A |
|4 |GP_B |
patient_care_home_tbl:
|nhs_number|care_home_date|
| -------- | ------------ |
|1 |1/5/2000 |
|1 |1/10/2010 |
|4 |26/10/2017 |
At the end, I'd like the query to return the following:
|nhs_number|Care_home|practice|
| -------- | ------- | ------ |
|1 |TRUE |GP_A |
|2 |FALSE | |
|3 |FALSE | |
|4 |TRUE |GP_A [or GP_B] |
I've update the query with CTE,
;WITH cte1 AS ---select all results
(
SELECT DISTINCT nnt.nhs_number,
CASE WHEN pcht.care_home_date IS NULL
THEN 'FALSE'
ELSE 'TRUE'
END AS CareHome,
ppt.practice,
rank()OVER(PARTITION BY nnt.nhs_number ORDER BY ppt.practice) AS R
FROM #nhs_no_tbl nnt
LEFT JOIN #patient_practice_tbl ppt ON nnt.nhs_number = ppt.nhs_number
LEFT JOIN #patient_care_home_tbl pcht ON nnt.nhs_number = pcht.nhs_number
),
CTE2 AS ---choose who may have multiple pracices
(
SELECT nhs_number
FROM CTE1
WHERE R = 2
),
CTE3 AS --- combine GP_A and GP_B
(
SELECT t.nhs_number,STRING_AGG(val,',') AS Practices
FROM
(
SELECT DISTINCT val = cte1.practice, CTE1.nhs_number
FROM #nhs_no_tbl nnt
INNER JOIN CTE2 ON nnt.nhs_number = CTE2.nhs_number
INNER JOIN cte1 ON nnt.nhs_number = CTE1.nhs_number
) t
--RIGHT JOIN #nhs_no_tbl nnt ON t.nhs_number = nnt.nhs_number
GROUP BY t.nhs_number
)
SELECT cte1.nhs_number,cte1.carehome,cte1.practice, cte3.Practices
FROM CTE1
LEFT JOIN cte3 ON cte1.nhs_number = CTE3.nhs_number
The result would be
then next you could store the result into a temp table and update temp table where practices is not null.

Update a column value within a SELECT query

I have a complicated SQL question.
Can we update a column within a SELECT query? Example:
Consider this table:
|ID |SeenAt |
----------------
|1 |20 |
|1 |21 |
|1 |22 |
|2 |70 |
|2 |80 |
I want a SELECT Query that gives for each ID when was it seen for the first time. And when did it seen 'again':
|ID |Start |End |
---------------------
|1 |20 |21 |
|1 |20 |22 |
|1 |20 |22 |
|2 |70 |80 |
|2 |70 |80 |
First, both columns Start and End would have the same value, but when a second row with the same ID is seen we need to update its predecessor to give End the new SeenAt value.
I succeeded to create the Start column, I give the minimum SeenAt value per ID to all IDs. But I can't find a way to update the End column everytime.
Don't mind the doubles, I have other columns that change in every new row
Also, I am working in Impala but I can use Oracle.
I hope that I have been clear enough. Thank you
You could use lead() and nvl():
select id, min(seenat) over (partition by id) seen_start,
nvl(lead(seenat) over (partition by id order by seenat), seenat) seen_end
from t
demo
Start is easy just the MIN of the GROUP
End you need to find the MIN after the SeenAt and in case you don't find it then the current SeenAt
SQL DEMO
SELECT "ID",
(SELECT MIN("SeenAt")
FROM Table1 t2
WHERE t1."ID" = t2."ID") as "Start",
COALESCE(
(SELECT MIN("SeenAt")
FROM Table1 t2
WHERE t1."ID" = t2."ID"
AND t1."SeenAt" < t2."SeenAt")
, t1."SeenAt"
) as End
FROM Table1 t1
OUTPUT
| ID | START | END |
|----|-------|-----|
| 1 | 20 | 21 |
| 1 | 20 | 22 |
| 1 | 20 | 22 |
| 2 | 70 | 80 |
| 2 | 70 | 80 |
you seem to need min() analytic function with a self-join:
select distinct t1.ID,
min(t1.SeenAt) over (partition by t1.ID order by t1.ID) as "Start",
t2.SeenAt as "End"
from tab t1
join tab t2 on t1.ID=t2.ID and t1.SeenAt<=t2.SeenAt
order by t2.SeenAt;
Demo

Better way of writing my SQL query with conditional group by

Here's my data
|vendorname |total|
---------------------
|Najla |10 |
|Disney |20 |
|Disney |10 |
|ToysRus |5 |
|ToysRus |1 |
|Gap |1 |
|Gap |2 |
|Gap |3 |
|Najla |2 |
Here's the resultset I want
|vendorname |grandtotal|
---------------------
|Disney |30 |
|Gap |6 |
|ToysRus |6 |
|Najla |2 |
|Najla |10 |
If the vendorname = 'Najla' I want individual rows with their respective total otherwise I would like to group them and return a sum of their totals.
This is my query--
select *
from
(
select vendorname, sum(total) grandtotal
from vendor
where vendorname<>'Najla'
group by vendorname
union all
select vendorname, total grandtotal
from vendor
where vendorname='Najla'
) A
I was wondering if there's a better way to write this query instead of repeating it twice and performing a union. Is there a condensed way to group some rows "conditionally".
Honestly, I think the union all version is going to be the best performing and easiest to read option if it has appropriate indexes.
You could, however, do something like this (assuming you have a unique id on your table):
select vendorname, sum(total) grandtotal
from t
group by
vendorname
, case when vendorname = 'Najla' then id else null end
rextester demo: http://rextester.com/OGZQ33364
returns
+------------+------------+
| vendorname | grandtotal |
+------------+------------+
| Disney | 30 |
| Gap | 6 |
| ToysRus | 6 |
| Najla | 10 |
| Najla | 2 |
+------------+------------+

SQL Insert Query For Multiple Max IDs

Table w:
|ID|Comment|SeqID|
|1 |bajg | 1 |
|1 |2423 | 2 |
|2 |ref | 1 |
|2 |comment| 2 |
|2 |juk | 3 |
|3 |efef | 1 |
|4 | hy | 1 |
|4 | 6u | 2 |
How do I insert a standard new comment for each ID for a new SeqID (SeqID increase by 1)
The Below query results in the highest SeqID:
Select *
From w
Where SEQID =
(select max(seqid)
from w)
Table w:
|2 |juk | 3 |
Expected Result
Table w:
|ID|Comment|SeqID|
|1 |sqc | 3 |
|2 |sqc | 4 |
|3 |sqc | 2 |
|4 |sqc | 3 |
Will I have to go through and insert all the values (new comment as sqc) I want into the table using the below, or is there a faster way?
INSERT INTO table_name
VALUES (value1,value2,value3,...);
Try this:
INSERT INTO mytable (ID, Comment, SeqID)
SELECT ID, 'sqc', MAX(SeqID) + 1
FROM mytable
GROUP BY ID
Demo here
You are probably better off just calculating the value when you query. Define an identity column on the table, say CommentId and run a query like:
select id, comment,
row_number() over (partition by comment order by CommentId) as SeqId
from t;
What is nice about this approach is that the ids are always sequential, you don't have no opportunities for duplicates, the table does not have to be locked to when inserting, and the sequential ids work even for updates and deletes.