Select / Merge user specific rows with additional fallback rows in PostgreSQL - sql

Setup: Postgresql table with a customer_id and a request_id column (+ additional not relevant data).
The rows with customer_id set to NULL work as a fallback/default.
Example what the table looks like:
Goal: I want to select all rows from the table for a given customer (e.g. where customer_id = 2).
For any existent request_id: If there are no entries for the given customer, return the fallback rows (where customer is null).
So the result should look like this:
Any idea how to write the select statement for postgresql? I'm kind of stuck and couldn't really find anything helpful so far. Thanks!

This is a strange requirement.
select t.*
from t
where t.customer_id = 2 or
(t.customer_id is null and
not exists (select 1 from t t2 where t2.request_id = t.request_id and t2.customer_id = 2)
);
For performance, I would recommend an index on (request_id, customer_id).

Related

How to use LIMIT and IN together to have a default row in SQL?

I am exploring SQL with W3School page and I have this requirements where I need to limit the query to a certain number but also having a default row included with that limit.
Here I want a default row where the customer name is Alfreds, then grab the remaining 29 rows to complete the query regardless of what their name is.
I tried to look on other SO question but they are too complicated to understand and using different syntax.
What you are looking for is a specific order clause.
Try this
SELECT * FROM Customers order by (case when CustomerName in ('Alfreds Futterkiste') then 0 else CustomerId end) limit 30 ;
If you're going to have a default row in SQL you should really have that row in the table with a known primary key, and then UNION it onto your query:
--default row, that is always included as long as the table has a PK 1
SELECT *
FROM Customers
WHERE CustomerId = 1
UNION ALL
--other rows, a variable number of
SELECT *
FROM Customers
WHERE CustomerId <> 1 AND ...
LIMIT 30
The limit presented in this way applies to the result of the Union
If you ever want to do something where you're unioning together limited sets in other combinations you might want to look at eg a form like
(... LIMIT 2)
UNION ALL
(... LIMIT 28)
Use UNION to combine the two queries.
SELECT *
FROM Customers
WHERE CustomerName != 'Alfredo Futterkiste'
LIMIT 9
UNION
SELECT *
FROM Customers
WHERE CustomerName = 'Alfreo Futterkiste'

SQL WHERE Clausule to get rows depending on the database content

Use case:
I have the customer_id and the task_id.
The database will always contain registers with a filled customer_id and empty task_id.
Sometimes will have the task_id filled. (as the example below)
Example 1
SELECT *
FROM table
WHERE customer_id = 11422412
AND task_id = 28870055
Here I expect to return the last two rows.
Example 2
SELECT *
FROM table
WHERE customer_id = 11432515
AND task_id = 22256884
Here I expect to return the only empty row.
Question:
How do I create a SQL Query to make sure that, in case the task_id exists in the database, I only return the records with task_id?
You could do something like the following with LIMIT. This will match the empty task_id and the set task_id (if it exists), order them so that the row with non-empty task_id comes first (if it exists), then return only the first one. (NULLS LAST is default sorting behavior in Postgre)
SELECT *
FROM table
WHERE customer_id = 11432515
AND (task_id = 22256884 OR task_id IS NULL)
ORDER BY task_id
LIMIT 1
I am assuming that you always want exactly one row like in your examples.
But there are other ways of doing it depending on your specific scenario (if your final query is more complicated than your examples).
Edited to add another way to handle case where more than one row matches customer_id and task_id:
SELECT *
FROM table t1
WHERE customer_id = 11432515
AND (task_id = 22256884
OR (
task_id is null
AND NOT EXISTS (SELECT * FROM table t2 WHERE t2.customer_id = 11432515 AND t2.task_id = 22256884)
)
)
This doesn't look super elegant, but it should work and you could use it as a starting point at least.

Merge 2 tables and pull the latest record from them

Say I have two customer tables which have pretty much the same columns. One of them is a temporal one which periodically updates the other one. That is, updates and additions to the records are done to the temporal table only. Say they're names are CUSTOMER, and CUSTOMER_TEMP.
The tables got information like ID, NAME, LAST_NAME, and ADDRESS.
This temporal table has three extra fields, TEMP_ID, RECORD_TYPE, and DATE. The record type is used to record whether there was an addition or an update. So the thing is I need to select the latest record from both tables. That involves several cases
Main table has record, but temporal doesn't -> Select main table record.
Main table has no record, but temporal does -> Select latest temporal table record.
Main table has record and temporal has an add record -> Select temporal table record.
Main table has record and temporal table has update record -> Select temporal table record.
Main table has record and temporal table has add and update record. -> Select temporal table update record.
Main table has record and temporal table has various update records. -> Select latest temporal table update record.
Main table has record and temporal table has add record and various update records. -> Select latest temporal table update record.
Now, I don't know whether this is a good flow or not. I was just told to make the query, so I don't have access to the DB, I believe I could make suggestions though. The thing is My SLQ knowledge is not enough to build this query. I know there's an INNER_JOIN involved, as well as a filter by date, and probably and EXIST, to check whether the record exist or not in the CUSTOMER_TEMP table. But I don't quite know how to build it. I'm working on .Net And SQLServer. Any help on it is quite appreciated.
select m.*, 0 as [rn]
from main m
where not exists (select 1 from temp where temp.id = m.id)
union
select tt.*
from ( select temp.*
, row_number() over (partition by id order by RECORD_TYPE desc, date desc) as rn
from temp
-- join main
-- on temp.ID = main.ID
) tt
where tt.rn = 1
if update does not sort last then need to do a trick like in the answer from Tom H
;WITH CTE_Latest_Temporal AS
(
SELECT
id,
name,
..., -- Put the rest of your columns here
ROW_NUMBER OVER (PARTITION BY id
ORDER BY
CASE record_type
WHEN 'Update' THEN 0
ELSE 1
END, date DESC) AS row_num
FROM
Customer_Temp
)
SELECT
M.id,
CASE WHEN T.id IS NOT NULL THEN T.name ELSE M.name END AS name,
... -- Similar CASE statements for the rest of your columns
FROM
Customer M
LEFT OUTER JOIN CTE_Latest_Temporal T ON
T.id = M.id AND
T.row_num = 1
The CASE statements can be replaced by a simple COALESCE(T.column, M.column) for any columns that cannot be NULL. I had to use the CASE to cover situations where the row might exist in the temp table but the column might be NULL in the temp table, but have a value in the main table.

Getting the last record in SQL in WHERE condition

i have loanTable that contain two field loan_id and status
loan_id status
==============
1 0
2 9
1 6
5 3
4 5
1 4 <-- How do I select this??
4 6
In this Situation i need to show the last Status of loan_id 1 i.e is status 4. Can please help me in this query.
Since the 'last' row for ID 1 is neither the minimum nor the maximum, you are living in a state of mild confusion. Rows in a table have no order. So, you should be providing another column, possibly the date/time when each row is inserted, to provide the sequencing of the data. Another option could be a separate, automatically incremented column which records the sequence in which the rows are inserted. Then the query can be written.
If the extra column is called status_id, then you could write:
SELECT L1.*
FROM LoanTable AS L1
WHERE L1.Status_ID = (SELECT MAX(Status_ID)
FROM LoanTable AS L2
WHERE L2.Loan_ID = 1);
(The table aliases L1 and L2 could be omitted without confusing the DBMS or experienced SQL programmers.)
As it stands, there is no reliable way of knowing which is the last row, so your query is unanswerable.
Does your table happen to have a primary id or a timestamp? If not then what you want is not really possible.
If yes then:
SELECT TOP 1 status
FROM loanTable
WHERE loan_id = 1
ORDER BY primaryId DESC
-- or
-- ORDER BY yourTimestamp DESC
I assume that with "last status" you mean the record that was inserted most recently? AFAIK there is no way to make such a query unless you add timestamp into your table where you store the date and time when the record was added. RDBMS don't keep any internal order of the records.
But if last = last inserted, that's not possible for current schema, until a PK addition:
select top 1 status, loan_id
from loanTable
where loan_id = 1
order by id desc -- PK
Use a data reader. When it exits the while loop it will be on the last row. As the other posters stated unless you put a sort on the query, the row order could change. Even if there is a clustered index on the table it might not return the rows in that order (without a sort on the clustered index).
SqlDataReader rdr = SQLcmd.ExecuteReader();
while (rdr.Read())
{
}
string lastVal = rdr[0].ToString()
rdr.Close();
You could also use a ROW_NUMBER() but that requires a sort and you cannot use ROW_NUMBER() directly in the Where. But you can fool it by creating a derived table. The rdr solution above is faster.
In oracle database this is very simple.
select * from (select * from loanTable order by rownum desc) where rownum=1
Hi if this has not been solved yet.
To get the last record for any field from a table the easiest way would be to add an ID to each record say pID. Also say that in your table you would like to hhet the last record for each 'Name', run the simple query
SELECT Name, MAX(pID) as LastID
INTO [TableName]
FROM [YourTableName]
GROUP BY [Name]/[Any other field you would like your last records to appear by]
You should now have a table containing the Names in one column and the last available ID for that Name.
Now you can use a join to get the other details from your primary table, say this is some price or date then run the following:
SELECT a.*,b.Price/b.date/b.[Whatever other field you want]
FROM [TableName] a LEFT JOIN [YourTableName]
ON a.Name = b.Name and a.LastID = b.pID
This should then give you the last records for each Name, for the first record run the same queries as above just replace the Max by Min above.
This should be easy to follow and should run quicker as well
If you don't have any identifying columns you could use to get the insert order. You can always do it like this. But it's hacky, and not very pretty.
select
t.row1,
t.row2,
ROW_NUMBER() OVER (ORDER BY t.[count]) AS rownum from (
select
tab.row1,
tab.row2,
1 as [count]
from table tab) t
So basically you get the 'natural order' if you can call it that, and add some column with all the same data. This can be used to sort by the 'natural order', giving you an opportunity to place a row number column on the next query.
Personally, if the system you are using hasn't got a time stamp/identity column, and the current users are using the 'natural order', I would quickly add a column and use this query to create some sort of time stamp/incremental key. Rather than risking having some automation mechanism change the 'natural order', breaking the data needed.
I think this code may help you:
WITH cte_Loans
AS
(
SELECT LoanID
,[Status]
,ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS RN
FROM LoanTable
)
SELECT LoanID
,[Status]
FROM LoanTable L1
WHERE RN = ( SELECT max(RN)
FROM LoanTable L2
WHERE L2.LoanID = L1.LoanID)

How to get one common value from Database using UNION

2 records in above image are from Db, in above table Constraint are (SID and LINE_ITEM_ID),
SID and LINE_ITEM_ID both column are used to find a unique record.
My issues :
I am looking for a query it should fetch the recored from DB depending on conditions
if i search for PART_NUMBER = 'PAU43-IMB-P6'
1. it should fetch one record from DB if search for PART_NUMBER = 'PAU43-IMB-P6', no mater to which SID that item belong to if there is only one recored either under SID =1 or SID = 2.
2. it should fetch one record which is under SID = 2 only, from DB on search for PART_NUMBER = 'PAU43-IMB-P6', if there are 2 items one in SID=1 and other in SID=2.
i am looking for a query which will search for a given part_number depending on Both SID 1 and 2, and it should return value under SID =2 and it can return value under SID=1 only if the there are no records under SID=2 (query has to withstand a load of Million record search).
Thank you
Select *
from Table
where SID||LINE_ITEM_ID = (
select Max(SID)||Max(LINE_ITEM_ID)
from table
where PART_NUMBER = 'PAU43-IMB-P6'
);
If I understand correctly, for each considered LINE_ITEM_ID you want to return only the one with the largest value for SID. This is a common requirement and, as with most things in SQL, can be written in many different ways; the best performing will depend on many factors, not least of which is the SQL product you are using.
Here's one possible approach:
SELECT DISTINCT * -- use a column list
FROM YourTable AS T1
INNER JOIN (
SELECT T2.LINE_ITEM_ID,
MAX(T2.SID) AS max_SID
FROM YourTable AS T2
GROUP
BY T2.LINE_ITEM_ID
) AS DT1 (LINE_ITEM_ID, max_SID)
ON T1.LINE_ITEM_ID = DT1.LINE_ITEM_ID
AND T1.SID = DT1.max_SID;
That said, I don't recall seeing one that relies on the UNION relational operator. You could easily rewrite the above using the INTERSECT relational operator but it would be more verbose.
Well in my case it worked something like this:
select LINE_ITEM_ID,SID,price_1,part_number from (
(select LINE_ITEM_ID,SID,price_1,part_number from Table where SID = 2)
UNION
(select LINE_ITEM_ID,SID,price_1,part_number from Table SID = 1 and line_item_id NOT IN (select LINE_ITEM_ID,SID,price_1,part_number from Table SID = 2)))
This query solved my issue..........