SQL algorithm with three identifiers from one table - sql

Probably this will be really easy, but I can't figure out, how to get necessary values from my DB with one query. Just can't figure it out now. I'm going to make this query inside CodeIginiter system.
Table 'information' construction:
CREATE TABLE information (
planid int(11) NOT NULL,
production_nr int(11) NOT NULL,
status int(11) NOT NULL
);
Table 'information' content:
Necessary output:
I would like to get (at the best - with only one query, but if its not possible, then with multiple) all planid's where: ALL of this plan id's pruduction_nrs has status >= 3.
In this case, I would need to get these plandid's: 2 and 5 because each of these planid's ALL production_nrs has status greater or equal than 3.

select planid, production_nr
from information inf1
where not exists (select 1 from information inf2
where inf1.planid = inf2.planid
and status < 3)
You might consider amending the select clause (first row) according to your needs:
Add distinct (if the table PK includes status column)
Change the column list

Try this,
SELECT planid , production_nr FROM information
WHERE production_nr IN(SELECT production_nr FROM information) AND STATUS >=3

Your problem is known as relational division. There are basically two ways to approach it
1) planid where it does not exist a production_nr with status < 3
select planid
from information i1
where not exists (
select 1 from information i2
where i1.planid = i2.planid
and i2.planid < 3
)
2) planid where the number of production_nr is equal to the number of production_nr with status >= 3. I'll leave that as an exercise ;-)

Related

For each existing row in table A, insert N rows in table B

I am trying to write a migration script using SQL.
I got the DDL part pretty easily covered, but now i have to migrate the existing data as shown below:
Before Migration:
Table BOX
uuid
active_service_number
other BOX columns...
2869c64f-8ecb-4296-8c3b-1c72b308d59f
2
...
After Migration:
Table BOX
uuid
other BOX columns...
2869c64f-8ecb-4296-8c3b-1c72b308d59f
...
Table BOX_SERVICE
uuid
number
state
box_uuid
6a33d57f-e02b-4d0a-b258-3cef0bb3dff7
0
INACTIVE
2869c64f-8ecb-4296-8c3b-1c72b308d59f
...
1
INACTIVE
2869c64f-8ecb-4296-8c3b-1c72b308d59f
...
2
ACTIVE
2869c64f-8ecb-4296-8c3b-1c72b308d59f
...
...
...
...
...
N
INACTIVE
2869c64f-8ecb-4296-8c3b-1c72b308d59f
To sum it up
For each BOX row, I want to create exactly N BOX_SERVICE rows, numbered from 0 to N. (N is a fixed number predetermined)
Only one BOX_SERVICE should have it's state set to ACTIVE, the one corresponding to active_service_number (2 in the example above) of the corresponding BOX.
I got the pseudo-code figured out, but can't seem to translate it into SQL. I tried to do it with a single request, with multiple requests, cursors.
Here is what my DDL is looking like:
CREATE TABLE BOX_SERVICE (uuid varchar(255) NOT NULL,
number int,
state varchar(255) DEFAULT 'INACTIVE',
box_uuid varchar(255),
PRIMARY KEY(uuid),
CONSTRAINT FK_BOX_BOX_SERVICE FOREIGN KEY (box_uuid)
REFERENCES BOX(uuid)
);
-- Migrate existing data
ALTER TABLE BOX DROP active_service_number
You can generate the rows using generate_series() and cross join:
select <uuid function>,
gs.n as number,
(case when gs.n = b.active_service_number then 'ACTIVE' else 'INACTIVE' end),
b.uuid as box_uuid
from box b cross join
generate_series(0, n, 1) as gs(n);
Your version of Postgres only offers UUIDs via an extension. Use whatever method you use for the first column of generating UUIDs.
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
select uuid_generate_v1(),sequence number,
(case when active_service_number=SEQUENCE THEN 'ACTIVE' ELSE 'INACTIVE' END)STATE,
UUID BOX_UUID from box CROSS JOIN (select generate_series(0,5) as sequence)t
Output:
UUID |Number|Status |Box_UUID
4b247576-8e42-11eb-80ae-1831bf6eced8|0 |INACTIVE|2869c64f-8ecb-4296-8c3b-1c72b308d59f
4b247577-8e42-11eb-80af-1831bf6eced8|1 |INACTIVE|2869c64f-8ecb-4296-8c3b-1c72b308d59f
4b247578-8e42-11eb-80b0-1831bf6eced8|2 |ACTIVE |2869c64f-8ecb-4296-8c3b-1c72b308d59f
4b247579-8e42-11eb-80b1-1831bf6eced8|3 |INACTIVE|2869c64f-8ecb-4296-8c3b-1c72b308d59f
4b24757a-8e42-11eb-80b2-1831bf6eced8|4 |INACTIVE|2869c64f-8ecb-4296-8c3b-1c72b308d59f
4b24757b-8e42-11eb-80b3-1831bf6eced8|5 |INACTIVE|2869c64f-8ecb-4296-8c3b-1c72b308d59f

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.

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

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).

Selecting batches of rows

I have a table widget_events that records event_what events occurring to
widget widget_id on date event_when. It's possible for the same event to
occur multiple times to the same widget on the same day. For this reason,
column event_id is used as primary key to distinguish such rows. Here is
the table declaration:
CREATE TABLE widget_events
(
event_id int4 UNIQUE NOT NULL,
event_when date NOT NULL,
event_what text NOT NULL,
widget_id int4 REFERENCES widgets (widget_id) NOT NULL,
PRIMARY KEY (event_id)
);
The client application processes events in batches, where each batch consists
of all events for one widget on one date. However, the application has no
previous knowledge of which widgets and dates are stored in widget_events.
One possible solution is to start by selecting one random row from
widget_events (using SQL's LIMIT), and then do another query for all
rows with the same widget_id and widget_when. After this batch is
processed, those rows can be deleted from widget_events, and we go back
to the first step. The algorithm stops when the first step reports that
there is no more random row to return.
My question is whether there is a faster, more elegant way to do this.
Is it possible in SQL (in particular the SQL understood by PostgreSQL)
to return each distinct batch in a single query?
To select distinct batches:
select distinct event_when
, event_what
from widget_events
Or you could pick up a single batch in one query, like:
select batch.*
from widget_events batch
join (
select event_when
, event_what
from widget_events
limit 1
) filter
on filter.event_when = batch.event_when
and filter.event_what = batch.event_what
Why don't you just return the rows, ordered by event_when:
select *
from widget_events we
order by event_when, event_what, event_id
I threw in event_what as well, so all similar events will be on consecutive rows.
Your logic can then just look for when the date changes to determine whether something is the last event. You could even put this into the select, if you wanted:
select *,
(case when lag(event_when) over (partition by event_id) < event_when then 1
else 0
end) as isFirst,
(case when lead(event_when) over (partition by event_id) < event_when then 1
else 0
end) as isLast
from widget_events we
order by event_when, event_what, event_id

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..........