कच्चा SQL उदाहरण देता है


239

मैं इस कोड को कच्चे sql में कैसे बदल सकता हूं और रेल में उपयोग कर सकता हूं? क्योंकि जब मैं इस कोड को हरोकू में तैनात करता हूं, तो एक अनुरोध टाइमआउट त्रुटि है। मुझे लगता है कि अगर मैं कच्चे sql का उपयोग करता हूं तो यह तेजी से होगा।

@payments = PaymentDetail.joins(:project).order('payment_details.created_at desc')
@payment_errors = PaymentError.joins(:project).order('payment_errors.created_at desc')

@all_payments = (@payments + @payment_errors)

3
आपको क्यों लगता है कि कच्ची एसक्यूएल तेज होगी? आप इसे SQL समस्या कैसे जानते हैं?
जॉन नेगल

क्वेरी प्लान को देखे बिना मैं यह अनुमान लगाऊंगा कि आप जो समस्या बना रहे हैं, वह create_at द्वारा आदेश दे रहा है। आप शायद उन संपूर्ण तालिकाओं में एक ओह स्कैन कर रहे हैं (ओह और परियोजना तालिका में भी ला रहे हैं)। एक बड़ी तालिका पर एक बार नियंत्रक विधि में उन दो में से एक करना और डीबी (हेरोकू डेटाबेस को सामान्य रूप से ट्यून किया जाता है, और आपके द्वारा खर्च किए गए $ $ के लिए अपेक्षाकृत कम शक्ति) आपको टाइमआउट मिलने की संभावना है। यदि आप इन तालिकाओं में बहुत सारे आवेषण नहीं कर रहे हैं तो आप एक अनुक्रमित सूचकांक कर सकते हैं: devcenter.heroku.com/articles/postgresql-indexes#sorted-indexes
जिम रेबेल

1
खैर यह हमेशा तेजी से sql को काटने के लिए होगा (यदि आप जानते हैं कि आप क्या कर रहे हैं)। रेल कुछ वास्तव में गंदा sql बनाती है। यह अच्छी तरह से काम करता है, लेकिन सामान्य होने के लिए ... सामान्य होना चाहिए। जिम ने कहा कि यह सही है। इंडेक्सिंग बनाना बेहतर होगा। Pgadmin में अपनी क्वेरी चलाने की कोशिश करें (अपने db के विरुद्ध)
baash05

जवाबों:


427

तुम यह केर सकते हो:

sql = "Select * from ... your sql query here"
records_array = ActiveRecord::Base.connection.execute(sql)

records_array इसके बाद एक सरणी में आपकी sql क्वेरी का परिणाम होगा जिसके माध्यम से आप पुनरावृति कर सकते हैं।


52
साइड नोट: records_arrayविभिन्न डेटाबेस एडेप्टर के लिए विभिन्न प्रकार के होंगे। यदि आप पीजी का उपयोग कर रहे हैं, तो यह एक उदाहरण होगा PG::Result, नहीं Array
tybro0103

58
और फिर आपको परिणाम सरणी प्राप्त करने के लिए valuesइस PG::Resultऑब्जेक्ट पर कॉल करना होगा
jobwat

3
@ baash05 आपको "क्लास के माध्यम से इसे एक्सेस करने के पक्ष में" से क्या मतलब है। - यह एक मॉडल के बिना तालिका तक पहुंचने के तरीके के बारे में बात करता है
यो-लुके

1
वास्तव में ओपी में एक मॉडल का उपयोग नहीं करने का कोई उल्लेख नहीं है। बस कैसे कच्चे sql निष्पादित करने के लिए। PaymentDetail.execute (sql) काम करता है।
baash05

20
मुझे पसंद है ActiveRecord::Base.connection.exec_queryबेहतर है क्योंकि यह एक रिटर्न ActiveRecords::Resultवस्तु जो की तरह काम के तरीकों है .columnsऔर .rowsपहुँच हेडर और मूल्यों के लिए। से हैश की सरणी .executeसे निपटने के लिए परेशानी हो सकती है और मुझे अनावश्यक परिणाम दिए गए जब मैं एक एसयूएम ग्रुप बाय क्लॉज के साथ भाग गया।
फ्रांको रोड्रिग्स

181

मुझे पता है कि यह पुराना है ... लेकिन मुझे आज भी यही समस्या थी और इसका हल मिला:

Model.find_by_sql

यदि आप परिणाम तुरंत चाहते हैं:

Client.find_by_sql("
  SELECT * FROM clients
  INNER JOIN orders ON clients.id = orders.client_id
  ORDER BY clients.created_at desc
")
# => [<Client id: 1, first_name: "Lucas" >, <Client id: 2, first_name: "Jan">...]

Model.connection.select_all('sql').to_hash

यदि आप केवल मूल्यों का हैश चाहते हैं:

Client.connection.select_all("SELECT first_name, created_at FROM clients
   WHERE id = '1'").to_hash
# => [
  {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"},
  {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}
]

परिणाम वस्तु:

select_allकोई resultवस्तु लौटाता है । आप इसके साथ जादू की चीजें कर सकते हैं।

result = Post.connection.select_all('SELECT id, title, body FROM posts')
# Get the column names of the result:
result.columns
# => ["id", "title", "body"]

# Get the record values of the result:
result.rows
# => [[1, "title_1", "body_1"],
      [2, "title_2", "body_2"],
      ...
     ]

# Get an array of hashes representing the result (column => value):
result.to_hash
# => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
      {"id" => 2, "title" => "title_2", "body" => "body_2"},
      ...
     ]

# ActiveRecord::Result also includes Enumerable.
result.each do |row|
  puts row['title'] + " " + row['body']
end

सूत्रों का कहना है:

  1. ActiveRecord - एसक्यूएल द्वारा फाइंडिनिग
  2. रूबी ऑन रेल्स - सक्रिय रिकॉर्ड परिणाम

9
#find_by_sqlवास्तव में मैं क्या चाहता था, बहुत बहुत धन्यवाद।
शेल्वैकू

#find_by_sql है !!
स्टीवन एल।

28

आप कच्ची क्वेरी का उपयोग करके निष्पादित कर सकते हैं ActiveRecord। और मैं एसक्यूएल ब्लॉक के साथ जाने का सुझाव दूंगा

query = <<-SQL 
  SELECT * 
  FROM payment_details
  INNER JOIN projects 
          ON projects.id = payment_details.project_id
  ORDER BY payment_details.created_at DESC
SQL

result = ActiveRecord::Base.connection.execute(query)

हाय .. क्या इस क्वेरी में पैरामीटर पास करने का कोई तरीका है?
अक्षय गोयल

@ अक्षय गोयल आप ब्लॉक के भीतर सामान्य रूबी स्ट्रिंग प्रक्षेप का उपयोग कर सकते हैं। SELECT * FROM users WHERE users.id = #{ user.id }
नाथन बेक

2
हालांकि आप यह कर सकते हैं कि यह आपको SQL इंजेक्शन की संभावना को उजागर कर सकता है
दीपक महाकाले

26

आप दोनों तालिकाओं के लिए एक ही क्वेरी के लिए प्रत्यक्ष SQL कर सकते हैं। मैं आशा करता हूं कि लोगों को सीधे स्ट्रिंग (स्वयं इंजेक्शन इंजेक्शन) में वेरिएबल डालने से एक पवित्र क्वेरी उदाहरण प्रदान करेगा, हालांकि यह उदाहरण इसके लिए आवश्यकता को निर्दिष्ट नहीं करता है:

@results = []
ActiveRecord::Base.connection.select_all(
  ActiveRecord::Base.send(:sanitize_sql_array, 
   ["... your SQL query goes here and ?, ?, ? are replaced...;", a, b, c])
).each do |record|
  # instead of an array of hashes, you could put in a custom object with attributes
  @results << {col_a_name: record["col_a_name"], col_b_name: record["col_b_name"], ...}
end

संपादित करें : जैसा कि हुआ ने कहा, एक सरल तरीका है ActiveRecord::Base.connection.execute("...")। दूसरा तरीका है ActiveRecord::Base.connection.exec_query('...').rows। और आप मूल तैयार किए गए कथनों का उपयोग कर सकते हैं, उदाहरण के लिए यदि पोस्टग्रेज का उपयोग करते हुए, तैयार कथन raw_connection, तैयार, और exec_prepared के साथ किया जा सकता है जैसा कि https://stackoverflow.com/a/13806512/178651 में वर्णित है।

आप ActiveRecord संबंधपरक प्रश्नों में कच्चे SQL अंश भी डाल सकते हैं: http://guides.rubyonrails.org/active_record_querying.html और संघों, स्कोप्स आदि में, आप संभवतः ActiveRecord संबंधपरक प्रश्नों के लिए एक ही SQL का निर्माण कर सकते हैं और ठंडी चीजों के साथ कर सकते हैं। एर्ल ने एर्नी के रूप में http://erniemiller.org/2010/03/28/advanced-activerecord-3-queries-with-arel/ में उल्लेख किया है । और, बेशक, अन्य ओआरएम, रत्न आदि हैं।

यदि यह बहुत उपयोग किया जा रहा है और सूचकांक जोड़ने से अन्य प्रदर्शन / संसाधन समस्याएँ पैदा नहीं होंगी, तो भुगतान_details.created_at के लिए DB में एक सूचकांक जोड़ने पर विचार करें और payment_errors.created_at के लिए।

यदि बहुत सारे रिकॉर्ड और सभी रिकॉर्ड एक साथ दिखाने की आवश्यकता नहीं है, तो पृष्ठांकन का उपयोग करने पर विचार करें:

यदि आपको पृष्ठांकित करने की आवश्यकता है, तो पहले DB_ में एक दृश्य बनाने के बारे में विचार करें, जिसे payment_records कहा जाता है, जो payment_details और payment_errors तालिकाओं को जोड़ता है, फिर दृश्य के लिए एक मॉडल है (जो केवल पढ़ने के लिए होगा)। कुछ DBs भौतिक विचारों का समर्थन करते हैं, जो प्रदर्शन के लिए एक अच्छा विचार हो सकता है।

रेल सर्वर और डीबी सर्वर, कॉन्फ़िगरेशन, डिस्क स्थान, नेटवर्क गति / विलंबता / आदि, निकटता, आदि पर हार्डवेयर या वीएम चश्मा पर भी विचार करें और यदि आप नहीं करते हैं, तो रेल एप्लिकेशन की तुलना में विभिन्न सर्वर / वीएम पर डीबी लगाने पर विचार करें। ।


चूँकि पूछने वाले को तारीख से छँटनी है, इसलिए मुझे लगता है कि पेजिनेशन से मदद नहीं मिलेगी? मैं एसक्यूएल पर सबसे गहरी नहीं हूं, लेकिन मेरी समझ यह है कि मूल पोस्ट में क्वेरी को क्रमबद्ध करने के लिए पूरी तालिका लाने की आवश्यकता होगी - केवल तभी यह परिणामों को सीमित कर सकता है। मुझे संदेह है कि क्वेरी प्लान का बड़ा हिस्सा ऑर्डर क्लॉज में है।
जिम Wrubel

@ JimWrubel ने पृष्ठांकन पर पैराग्राफ को स्पष्ट किया- शब्दांकन थोड़ा हटकर था।
गैरी एस। वीवर

2

मैं के साथ काम करना चाहते हैं exec_queryकी ActiveRecord, वर्ग, क्योंकि यह वस्तु में बदलने क्वेरी की मैपिंग देता है, तो यह वस्तुओं दोहराएं करने के लिए बहुत व्यावहारिक और उत्पादक हो जाता है जब विषय कच्चे एसक्यूएल है।

उदाहरण:

values = ActiveRecord::Base.connection.exec_query("select * from clients")
p values

और इस पूरी क्वेरी को वापस करें:

[{"id": 1, "name": "user 1"}, {"id": 2, "name": "user 2"}, {"id": 3, "name": "user 3"}]

केवल मूल्यों की सूची प्राप्त करने के लिए

p values.rows

[[1, "user 1"], [2, "user 2"], [3, "user 3"]]

केवल फ़ील्ड कॉलम प्राप्त करने के लिए

p values.columns

["id", "name"]

0

आप ActiveRecord शर्तों के साथ कच्ची SQL भी मिला सकते हैं, उदाहरण के लिए यदि आप किसी फ़ंक्शन को किसी स्थिति में कॉल करना चाहते हैं:

my_instances = MyModel.where.not(attribute_a: nil) \
  .where('crc32(attribute_b) = ?', slot) \
  .select(:id)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.