Usage of FOR UPDATE in window function - Postgres - sql

I'm working on Oracle to Postgres migration, The service is using my-batis for the DB connection, and have some complex queries that I couldn't convert with some reference.
Below is the table definition of which the query is to run.
CREATE TABLE TABLE1(
NOTIFY_ID bigint primary key not null,
TRANSACTION_ID numeric not null,
EVENT_TYPE character varying(64) not null,
EVENT_TIME timestamp(6) with time zone not null,
SOURCE_TRANSACTION_ID numeric,
QUANTITY numeric,
PROCESSED character varying(8) not null,
CURRENCY_CODE character varying(8),
COST numeric(12,2),
DESTINATION_LOCATION character varying(32),
SOURCE_LOCATION character varying(32),
CUSTOMER_COUNTRY_CODE character varying(2)
);
Here is the Oracle query which needs to be converted to PostgreSQL.
SELECT
TRANSACTION_ID,
EVENT_TYPE,
EVENT_TIME,
CUSTOMER_COUNTRY_CODE,
SOURCE_TRANSACTION_ID,
PROCESSED,
QUANTITY,
CURRENCY_CODE,
COST,
TABLE1.NOTIFY_ID,
DESTINATION_LOCATION,
SOURCE_LOCATION
FROM
(SELECT
NOTIFY_ID, RANK() OVER (ORDER BY TRANSACTION_ID, NOTIFY_ID) AS RANK
FROM
table1
WHERE
PROCESSED = 'FALSE' ) RANKED
INNER JOIN
TABLE1
ON RANKED.NOTIFY_ID = table1.NOTIFY_ID
WHERE
RANK <= 5
ORDER BY TRANSACTION_ID
FOR UPDATE OF TABLE1.NOTIFY_ID
SKIP LOCKED
When I run this query by removing the column name after for update it says "FOR UPDATE is not allowed with window functions." I'm not sure how to convert this query to Postgresql compatible.
Please help me in converting this query.
Thanks,
Gokul.

Related

Fetch data from two tables and add the values with same date in sqlite

I have two tables named as cash and bank both the tables have same attributes
`( `id` VARCHAR, `person_id` VARCHAR, `date` DATE, `remarks` VARCHAR, `type` VARCHAR, `amount` INTEGER, `balance` INTEGER, PRIMARY KEY(`id`)
)`
Now I need an SQLite query to fetch the total income/expenses of a particular date by adding the cash amount and bank amount
To get the amount sum based on the date and type, it can be done as below:
select type,date,sum(amount) total
from
(
select type,amount,date
from cash
union all
select type,amount,date
from bank
) t
group by type,SUBSTR(date,0,10)

Ambiguous column name SQL

I get the following error when I want to execute a SQL query:
"Msg 209, Level 16, State 1, Line 9
Ambiguous column name 'i_id'."
This is the SQL query I want to execute:
SELECT DISTINCT x.*
FROM items x LEFT JOIN items y
ON y.i_id = x.i_id
AND x.last_seen < y.last_seen
WHERE x.last_seen > '4-4-2017 10:54:11'
AND x.spot = 'spot773'
AND (x.technology = 'Bluetooth LE' OR x.technology = 'EPC Gen2')
AND y.id IS NULL
GROUP BY i_id
This is how my table looks like:
CREATE TABLE [dbo].[items] (
[id] INT IDENTITY (1, 1) NOT NULL,
[i_id] VARCHAR (100) NOT NULL,
[last_seen] DATETIME2 (0) NOT NULL,
[location] VARCHAR (200) NOT NULL,
[code_hex] VARCHAR (100) NOT NULL,
[technology] VARCHAR (100) NOT NULL,
[url] VARCHAR (100) NOT NULL,
[spot] VARCHAR (200) NOT NULL,
PRIMARY KEY CLUSTERED ([id] ASC));
I've tried a couple of things but I'm not an SQL expert:)
Any help would be appreciated
EDIT:
I do get duplicate rows when I remove the GROUP BY line as you can see:
I'm adding another answer in order to show how you'd typically select the lastest record per group without getting duplicates. You's use ROW_NUMBER for this, marking every last record per i_id with row number 1.
SELECT *
FROM
(
SELECT
i.*,
ROW_NUMBER() over (PARTITION BY i_id ORDER BY last_seen DESC) as rn
FROM items i
WHERE last_seen > '2017-04-04 10:54:11'
AND spot = 'spot773'
AND technology IN ('Bluetooth LE', 'EPC Gen2')
) ranked
WHERE rn = 1;
(You'd use RANK or DENSE_RANK instead of ROW_NUMBER if you wanted duplicates.)
You forgot the table alias in GROUP BY i_id.
Anyway, why are you writing an anti join query where you are trying to get rid of duplicates with both DISTINCT and GROUP BY? Did you have issues with a straight-forward NOT EXISTS query? You are making things way more complicated than they actually are.
SELECT *
FROM items i
WHERE last_seen > '2017-04-04 10:54:11'
AND spot = 'spot773'
AND technology IN ('Bluetooth LE', 'EPC Gen2')
AND NOT EXISTS
(
SELECT *
FROM items other
WHERE i.i_id = other.i_id
AND i.last_seen < other.last_seen
);
(There are other techniques of course to get the last seen record per i_id. This is one; another is to compare with MAX(last_seen); another is to use ROW_NUMBER.)

How can I make a report that does a group count based on a the first character of a column?

I have this table:
CREATE TABLE [dbo].[Phrase] (
[PhraseId] UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
[English] NVARCHAR (MAX) NOT NULL,
[Romaji] NVARCHAR (MAX) NULL,
[EnglishAscii] AS (ascii([English])) PERSISTED,
PRIMARY KEY CLUSTERED ([PhraseId] ASC)
);
What I would like to do is to get a report that looks something like this:
A 25
B 35
C 10
D 99
...
Y 3
All the strings in the English column have a first character that is uppercase.
Can someone give me some hints as to how I can do this kind of a report?
Use LEFT string function
Select Left([English],1), Count(1)
From Yourtable
Group by Left([English],1)
or you can use SUBSTRING string function
Select Substring([English],1,1), Count(1)
From Yourtable
Group by Substring([English],1,1)
Use the UPPER keyword in order to make the first character of the column [English] in to upper case ,if it is not.
SELECT UPPER(Left([English],1)) Col,
Count(1) CNT
FROM Yourtable
GROUP BY Left([English],1)
Have a derived table which simply returns that first character. GROUP BY it's result:
select letter, count(*)
from
(
select substring([English], 1, 1) as letter
from [dbo].[Phrase]
) dt
group by letter
This way you only have to write the substring expression once. Easier to write without errors, and easier and safer to maintain/update.

Sql Server 2012 error 8120. group by error on a simple select query [duplicate]

This question already has answers here:
GROUP BY / aggregate function confusion in SQL
(5 answers)
Closed 8 years ago.
This is my table creation code
CREATE TABLE "transactions" (
"id" INT NOT NULL,
"tno" INT NOT NULL,
"pno" INT NOT NULL,
"sno" INT NOT NULL,
"accode" INT NOT NULL,
"acname" VARCHAR(50) NOT NULL,
"date" DATE NOT NULL,
"truck" VARCHAR(50) NULL,
"weight" DECIMAL NULL,
"quality" INT NULL,
"debit" MONEY NOT NULL,
"credit" MONEY NOT NULL,
"amount" MONEY NOT NULL,
"comment" TEXT NULL,
PRIMARY KEY ("id")
);
I want to get some data from this table by this query.
Select * from transactions GROUP BY tno
but it gives me error:
Msg 8120, Level 16, State 1, Line 1
Column 'transactions.tno' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
finaly i got solution . my answer is
select yt.id, yt.tno, yt.accode, yt.acname, yt.pno, yt.comment, yt.amount, yt.date from transactions yt inner join( select tno, max(id) id from transactions ss group by tno ) ss on yt.id = ss.id and yt.tno = ss.tno order by yt.tno desc
GROUP BY allows you to collect data about mathematical operations and collect them under non-mathematical ones.
For example
SELECT truck, SUM(tno)
FROM transactions
GROUP BY truck
Would give you the total values of TNO field for each type of "truck" you have in your table. The mathematical operator here is SUM.
So if your table contained this:
truck tno
------------
dodge 44
viper 33
dodge 22
The above query would return
truck tno
------------
dodge 66
viper 33
You're using GROUP BY without a math operator, so the query fails. To just get your data use
Select * from transactions
Or if you do want to use a math operator to get some data, tell us more clearly what you're trying to achieve rather than just posting table structure and a failing query.

How can I join these tables?

I have a number of devices logging different data at different times and want to get all the data in a single query, ordered by time. An example of the kinds of tables I have:
CREATE TABLE location(
device_id INT, -- REFERENCES device(id)
timestamp DATETIME2 NOT NULL,
position GEOMETRY NOT NULL
)
CREATE TABLE temperature(
device_id INT, -- REFERENCES device(id)
timestamp DATETIME2 NOT NULL,
temp FLOAT NOT NULL
)
I want to have a single query that joins the tables on device_id and timestamp that contains nulls when the timestamps don't match. An example of the output format I am seeking is:
device_id, timestamp, location, temperature
1, 2011/12/1 10:00:00, (35.1, 139.2), NULL
1, 2011/12/1 10:00:01, NULL, 9.0
1, 2011/12/1 10:00:02, (35.1, 139.2), 9.1
I've tried doing FULL JOIN but cannot figure out how to do the timestamp column without a huge CASE statement (keep in mind although I've only shown 2 tables, this can have many more).
SELECT
location.device_id,
CASE WHEN location.timestamp IS NOT NULL THEN
location.timestamp
ELSE
temperature.timestamp
END as timestamp,
location,
temp
FROM
location
FULL JOIN temperature ON location.device_id = temperature.device_id
AND location.timestamp = temperature.timestamp
ORDER BY
timestamp
Is there a simpler way to write this kind of query?
You can use the COALESCE expression.
SELECT
location.device_id,
COALESCE(location.timestamp, temperature.timestamp) as timestamp,
position,
temp
FROM
location
FULL JOIN temperature ON location.device_id = temperature.device_id
AND location.timestamp = temperature.timestamp
ORDER BY
timestamp;
Yes, you can use an OUTER Join to the temperature table. That will return nulls in the case where there is no matching row in the temperature table.
You need a COALESCE to get the device_id/timestamp, as follows:
SELECT
COALESCE(l.device_id, t.device_id) as device_id,
COALESCE(l.timestamp, t.timestamp) as timestamp,
l.position as location,
t.temp as temperature
FROM location l
FULL JOIN temperature t ON l.device_id = t.device_id
AND l.timestamp = t.timestamp
ORDER BY 2
Also note the increased readability by aliasing the tables with very short names (l and t).
You may want to review your ordering - perhaps you want ORDER BY 1, 2 instead
SELECT device_id, timestamp, position, NULL AS temp
FROM location
UNION ALL
SELECT device_id, timestamp, NULL AS position, temp
FROM temperature
ORDER
BY timestamp;
Note the ALL keyword is required here.