পোস্টগ্রিএসকিউএল ফাংশনটি সিটিইর ভিতরে থেকে ডাকলে কার্যকর করা হয় না


16

কেবলমাত্র আমার পর্যবেক্ষণটি নিশ্চিত করার এবং কেন এটি হচ্ছে সে সম্পর্কে একটি ব্যাখ্যা পাওয়ার আশা করছি।

আমার হিসাবে একটি ফাংশন সংজ্ঞায়িত হয়েছে:

CREATE OR REPLACE FUNCTION "public"."__post_users_id_coin" ("coins" integer, "userid" integer) RETURNS TABLE (id integer) AS '
UPDATE
users
SET
coin = coin + coins
WHERE
userid = users.id
RETURNING
users.id' LANGUAGE "sql" COST 100 ROWS 1000
VOLATILE
RETURNS NULL ON NULL INPUT
SECURITY INVOKER

আমি যখন কোনও সিটিই থেকে এই ফাংশনটি কল করি তখন এটি এসকিউএল কমান্ড কার্যকর করে তবে কার্যটি ট্রিগার করে না , উদাহরণস্বরূপ:

WITH test AS
(SELECT * FROM __post_users_id_coin(10, 1))

SELECT
1 -- Select 1 but update not performed

অন্যদিকে, আমি যদি কোনও সিটিই থেকে ফাংশনটি কল করি এবং তারপরে সিটিই ফলাফল নির্বাচন করি (বা সিটিই ছাড়াই সরাসরি ফাংশনটি কল করি) এটি এসকিউএল আদেশটি কার্যকর করে এবং ফাংশনটি ট্রিগার করে, উদাহরণস্বরূপ:

WITH test AS
(SELECT * FROM __post_users_id_coin(10, 1))

SELECT
*
FROM
test -- Select result and update performed

অথবা

SELECT * FROM __post_users_id_coin(10,1)

যেহেতু আমি ফাংশনটির ফলাফল সম্পর্কে সত্যিই চিন্তা করি না (কেবল আপডেটটি সম্পাদন করা দরকার) সিটিইর ফলাফলটি না বাছাই করে কাজ করার কোনও উপায় আছে কি?

উত্তর:


12

এটাই প্রত্যাশিত আচরণ। সিটিই বাস্তবায়িত হয় তবে ব্যতিক্রম রয়েছে।

যদি কোনও সিটিই প্যারেন্ট ক্যোয়ারিতে রেফারেন্স না হয় তবে তা একেবারেই বাস্তবায়িত হয় না। আপনি উদাহরণস্বরূপ এটি চেষ্টা করতে পারেন এবং এটি কার্যকর হবে:

WITH not_executed AS (SELECT 1/0),
     executed AS (SELECT 1)
SELECT * FROM executed ;

কোডটি ক্রেইগ
রিঞ্জারের ব্লগ পোস্টে একটি মন্তব্য থেকে অনুলিপি করেছে: পোস্টগ্র্যাসএসকিউএল এর সিটিইগুলি অপ্টিমাইজেশন বেড়া


এটি এবং এই জাতীয় বেশ কয়েকটি প্রশ্নের চেষ্টা করার আগে আমি ভেবেছিলাম যে ব্যতিক্রম ছিল: "যখন কোনও সিটিই প্যারেন্ট ক্যোয়ারিতে বা অন্য কোনও সিটিইতে রেফারেন্স করা হয় না এবং নিজেই অন্য কোনও সিটিইতে রেফারেন্স দেয় না"। সুতরাং, যদি আপনি চান সিটিই কার্যকর করা হোক তবে ফলাফলগুলি কোয়েরির ফলাফলে প্রদর্শিত না হয়, আমি ভেবেছিলাম এটি একটি কার্যকরী হবে (এটি অন্য সিটিইতে রেফারেন্স করে)।

কিন্তু হায়রে, এটি আমার প্রত্যাশার মতো কার্যকর হয় না :

WITH test AS
    (SELECT * FROM __post_users_id_coin(10, 1)),
  execute_test AS 
    (TABLE test)
SELECT 1 ;     -- no, it doesn't do the update

এবং তাই, আমার "ব্যতিক্রম নিয়ম" সঠিক নয়। যখন কোনও সিটিই অন্য সিটিই দ্বারা রেফারেন্স করা হয় এবং সেগুলির কোনওটিই প্যারেন্ট ক্যোয়ারির দ্বারা উল্লেখ করা হয় না, পরিস্থিতি আরও জটিল হয় এবং আমি নিশ্চিত না যে ঠিক কী ঘটে এবং কখন সিটিই বাস্তবায়িত হয়। ডকুমেন্টেশনেও আমি এই জাতীয় মামলার কোনও রেফারেন্স পাই না।


আপনি ইতিমধ্যে প্রস্তাবিত ব্যবহারের চেয়ে ভাল সমাধান আমি দেখতে পাচ্ছি না:

SELECT * FROM __post_users_id_coin(10, 1) ;

বা:

WITH test AS
    (SELECT * FROM __post_users_id_coin(10, 1))
SELECT *
FROM test ;

যদি ফাংশনটি একাধিক সারি আপডেট করে এবং 1ফলস্বরূপ আপনি অনেকগুলি সারি (সহ ) পান তবে আপনি একটি একক সারি পেতে সমষ্টি করতে পারবেন:

SELECT MAX(1) AS result FROM __post_users_id_coin(10, 1) ;

তবে আমি SELECT *আপনার উদাহরণ হিসাবে ফাংশনটির একটি আপডেট ফিরে আসার ফলাফলগুলি পছন্দ করতে চাই , তাই যাই হোক না কেন এই ক্যোয়ারীটি জানে যে আপডেট রয়েছে কিনা এবং টেবিলের পরিবর্তনগুলি কী ছিল তা জানে।



5

এটি প্রত্যাশিত, দলিলযুক্ত আচরণ।

টম লেন এখানে এটি ব্যাখ্যা।

ম্যানুয়ালটিতে এখানে নথিবদ্ধ:

ডেটা-পরিবর্তন বিবৃতি মধ্যে WITHঠিক একবার মৃত্যুদন্ড কার্যকর করা হয়, এবং সবসময় সমাপ্তির কিনা স্বাধীনভাবে প্রাথমিক ক্যোয়ারী তাদের আউটপুট সমস্ত (বা প্রকৃতপক্ষে থাকে) পড়া হয়। লক্ষ্য করুন এই জন্য নিয়ম থেকে ভিন্ন যে SELECTমধ্যে WITH: যেমন পূর্বের বিভাগে বিবৃত, একটি কার্যকর SELECTশুধুমাত্র যতটা প্রাথমিক ক্যোয়ারী তার আউটপুট দাবী বাহিত হয়

বোল্ড জোর আমার। "ডাটা-পরিবর্তন" হয় INSERT, UPDATEএবং DELETEপ্রশ্নের। (বিরোধী হিসাবে SELECT।) ম্যানুয়ালটি আরও একবার:

আপনি (ডেটা-পরিবর্তন বিবৃতি ব্যবহার করতে পারেন INSERT, UPDATEঅথবা DELETE) এ WITH

সঠিক ফাংশন

CREATE OR REPLACE FUNCTION public.__post_users_id_coin (_coins integer, _userid integer)
  RETURNS TABLE (id integer) AS
$func$
UPDATE users u
SET    coin = u.coin + _coins  -- see below
WHERE  u.id = _userid
RETURNING u.id
$func$ LANGUAGE sql COST 100 ROWS 1000 STRICT;

আমি ডিফল্ট (শব্দ) ধারাগুলি বাদ দিয়েছি এবং STRICTএর সংক্ষিপ্ত প্রতিশব্দRETURNS NULL ON NULL INPUT

নিশ্চিত করুন যে কোনওভাবে প্যারামিটারের নামগুলি কলামের নামের সাথে বিরোধ নয় conflict আমি এটিকে চাপ দিয়েছি _, তবে এটি কেবল আমার ব্যক্তিগত পছন্দ।

যদি coinহতে পারে তবে NULLআমার পরামর্শ:

SET    coin = CASE WHEN coin IS NULL THEN _coins ELSE coin + _coins END

তাহলে users.id, প্রাথমিক চাবিকাঠি তারপর তন্ন তন্ন RETURNS TABLEনা ROWs 1000কোনো অর্থে দেখা যায়। কেবলমাত্র একটি একক সারি আপডেট / ফেরত যেতে পারে। তবে এগুলিই মূল পয়েন্টের পাশে।

যথাযথ কল

RETURNINGআপনি যদি যেভাবেই কলটিতে ফিরে আসা মানগুলিকে উপেক্ষা করতে চলেছেন তবে আপনার ফাংশনটি থেকে ক্লজটি এবং রিটার্ন মানগুলি ব্যবহার করার কোনও অর্থ নেই । SELECT * FROM ...যদি আপনি যাইহোক এড়াতে পারেন তবে ফিরে আসা সারিগুলি পচিয়ে ফেলার কোনও অর্থ নেই ।

কেবল একটি স্কেলার ধ্রুবক ( RETURNING 1) ফিরিয়ে দিন , ফাংশনটি RETURNS int(বা RETURNINGপুরোপুরি ড্রপ করে এটি তৈরি করুন RETURNS void) হিসাবে সংজ্ঞায়িত করুন এবং এর সাথে কল করুনSELECT my_function(...)

সমাধান

আপনি থেকে ...

ফলাফল সম্পর্কে সত্যিই চিন্তা করবেন না

.. কেবল SELECTএকটি ধ্রুবক ফর্ম সিটিই। এটি যতক্ষণ না বাইরের SELECT(প্রত্যক্ষ বা পরোক্ষভাবে) রেফারেন্স করা হয় ততক্ষণ তা কার্যকর করার গ্যারান্টিযুক্ত ।

WITH test AS (SELECT __post_users_id_coin(10, 1))
SELECT 1 FROM test;

যদি আপনার কাছে আসলে একটি সেট-রিটার্নিং ফাংশন থাকে এবং এখনও আউটপুটটি যত্ন করে না:

WITH test AS (SELECT * FROM __post_users_id_coin(10, 1))
SELECT 1 FROM test LIMIT 1;

1 সারির বেশি আর ফিরে আসার দরকার নেই। ফাংশনটি এখনও বলা হয়।

শেষ পর্যন্ত, আপনার কেন সিটিই শুরু করতে হবে তা স্পষ্ট নয়। সম্ভবত ধারণার একটি প্রমাণ।

ঘনিষ্ঠভাবে সম্পর্কিত:

এসও সম্পর্কিত সম্পর্কিত উত্তর:

এবং বিবেচনা করুন:


মারাত্মক, বড় ফ্যান এবং আপনার উত্তরটিও পেয়ে বেশ সম্মানিত ইরভিন। আমি সিটিই ব্যবহার করছি যেহেতু আমি একই মোড়ক ফাংশনটির মধ্যে INSERTপূর্বের কাজ করছি UPDATE- কোনও লেনদেন উপলভ্য নয়।
অ্যান্ডি

খুশী হলাম। শুধু AQ: হয় testমধ্যে WITH test AS (SELECT * FROM __post_users_id_coin(10, 1)) SELECT ... LIMIT 1;একটি পরিবর্তন কোটে বা না বলে মনে করা?
ypercubeᵀᴹ

@ ইয়পারক्यूबᵀᴹ: এ SELECTসিটিই পরিভাষা অনুযায়ী "ডেটা-সংশোধন" নয়। আমি উপরে কিছু স্পষ্টতা যুক্ত করেছি। ব্যবহারকারীর দায়িত্ব যদি (গুলি) কোনও ফাংশনে কোড যুক্ত করে যা পর্দার আড়ালে থাকা ডেটা সংশোধন করে।
এরউইন ব্র্যান্ডসটেটার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.