I joined 2 tables (Modes and Prices) and need to get data from both, but I'm getting error ambiguous column name 'min_weight'. How can I specify them in order to get rid of this error?
tables
Modes: id, name, min_weight, max_weight, fixed_fee
Prices: id, min_weight, max_weight, price_per_km, mode_id
queries
#prices = Price.all.where(mode_id: #modes.ids).where('min_weight <= ?', #products_weight).where('max_weight >= ?', #products_weight).select("price_per_km")
#modes_prices = #modes.joins(:prices).where(prices: {id: #prices.ids}).select("modes.*, prices.price_per_km")
expect
I expect to get from #modes_prices: mode.min_weight, mode.max_weight, price.price_per_km
I don't know Ruby nor its syntax, but error you got is relatively frequent in SQL. It says that - when you select rows from two (or more) tables that have columns with the same name, the SQL engine doesn't know which table's column you meant to use and it is ambiguous to it.
For example, in Oracle:
SQL> with
2 modes (id, name, min_weight) as --> both tables have MIN_WEIGHT
3 (select 1, 'apple', 10 from dual),
4 prices (id, min_weight) as --> column
5 (select 1, 20 from dual)
6 select min_weight --> OK, but - which one is it?
7 from modes join prices on modes.id = prices.id;
select min_weight
*
ERROR at line 6:
ORA-00918: column ambiguously defined
SQL>
Line #6 is select min_weight. What's wrong with it? Oracle doesn't know which min_weight you want to fetch: is it the one that belongs to modes, or the one that belongs to prices table (as both of them contain that column)?
Solution is to precisely instruct SQL what you wanted by preceding column name with its table name (or table's alias):
6 select prices.min_weight --> specify table name
7 from modes join prices on modes.id = prices.id;
MIN_WEIGHT
----------
20
SQL>
Line #6 is now select prices.min_weight and it is clear that I want value that belongs to prices table.
You should do the same. For example:
No : #prices = Price.all.where(mode_id: #modes.ids).where('min_weight <= ?'
Yes: #prices = Price.all.where(mode_id: #modes.ids).where('#modes.min_weight <= ?'
------
specify table name
Once again: I don't know Ruby and that maybe isn't "it", but - the root cause is the same - you should specify table name along with column name.
Related
In Google BigQuery, I would like to delete a subset of records, based on the value of a specific column. It's a query that I need to run repeatedly and that I would like to run automatically.
The problem is that this specific column is of the form STRUCT<column_1 ARRAY (STRING), column_2 ARRAY (STRING), ... >, and I don't know how to use such a column in the where-clause when using the delete-command.
Here is basically what I am trying to do (this code does not work):
DELETE
FROM dataset.table t
LEFT JOIN UNNEST(t.category.column_1) AS type
WHERE t.partition_date = '2020-07-22'
AND type = 'some_value'
The error that I'm getting is: Syntax error: Expected end of input but got keyword LEFT at [3:1]
If I replace the DELETE with SELECT *, it does work:
SELECT *
FROM dataset.table t
LEFT JOIN UNNEST(t.category.column_1) AS type
WHERE t.partition_date = '2020-07-22'
AND type = 'some_value'
Does somebody know how to use such a column to delete a subset of records?
EDIT:
Here is some code to create a reproducible example with some silly data (fill in your own dataset and table name in all queries):
Suppose you want to delete all rows where category.type contains the value 'food'.
1 - create a table:
CREATE TABLE <DATASET>.<TABLE_NAME>
(
article STRING,
category STRUCT<
color STRING,
type ARRAY<STRING>
>
);
2 - Insert data into the new table:
INSERT <DATASET>.<TABLE_NAME>
SELECT "apple" AS article, STRUCT('red' AS color, ['fruit','food'] as type) AS category
UNION ALL
SELECT "cabbage" AS article, STRUCT('blue' AS color, ['vegetable', 'food'] as type) AS category
UNION ALL
SELECT "book" AS article, STRUCT('red' AS color, ['object'] as type) AS category
UNION ALL
SELECT "dog" AS article, STRUCT('green' AS color, ['animal', 'pet'] as type) AS category;
3 - Show that select works (return all rows where category.type contains the value 'food'; these are the rows I want to delete):
SELECT *
FROM <DATASET>.<TABLE_NAME>
LEFT JOIN UNNEST(category.type) type
WHERE type = 'food'
Initial Result
4 - My attempt at deleting rows where category.type contains 'food' does not work:
DELETE
FROM <DATASET>.<TABLE_NAME>
LEFT JOIN UNNEST(category.type) type
WHERE type = 'food'
Syntax error: Unexpected keyword LEFT at [3:1]
Desired Result
This is the code I used to delete the desired records (the records where category.type contains the value 'food'.)
DELETE
FROM <DATASET>.<TABLE_NAME> t1
WHERE EXISTS(SELECT 1 FROM UNNEST(t1.category.type) t2 WHERE t2 = 'food')
The embarrasing thing is that I've seen these kind of answers on similar questions (for example on update-queries). But I come from Oracle-SQL and I think that there you are required to connect your subquery with your main query in the WHERE-statement of the subquery (ie. connect t1 with t2), so I didn't understand these answers. That's why I posted this question.
However, I learned that BigQuery automatically understands how to connect table t1 and 'table' t2; you don't have to explicitly connect them.
Now it is possible to still do this (perhaps even recommended?):
DELETE
FROM <DATASET>.<TABLE_NAME> t1
WHERE EXISTS (SELECT 1 FROM <DATASET>.<TABLE_NAME> t2 LEFT JOIN UNNEST(t2.category.type) AS type WHERE type = 'food' AND t1.article=t2.article)
but a second difficulty for me was that my ID in my actual data is somehow hidden in an array>struct-construction, so I got stuck connecting t1 & t2. Fortunately this is not always an absolute necessity.
Since you did not provide any sample data I am going to explain using some dummy data. In case you add your sample data, I can update the answer.
Firstly,according to your description, you have only a STRUCT not an Array[Struct <col_1, col_2>].For this reason, you do not need to use UNNEST to access the values within the data. Below is an example how to access particular data within a STRUCT.
WITH data AS (
SELECT 1 AS id, STRUCT("Alex" AS name, 30 AS age, "NYC" AS city) AS info UNION ALL
SELECT 1 AS id, STRUCT("Leo" AS name, 18 AS age, "Sydney" AS city) AS info UNION ALL
SELECT 1 AS id, STRUCT("Robert" AS name, 25 AS age, "Paris" AS city) AS info UNION ALL
SELECT 1 AS id, STRUCT("Mary" AS name, 28 AS age, "London" AS city) AS info UNION ALL
SELECT 1 AS id, STRUCT("Ralph" AS name, 45 AS age, "London" AS city) AS info
)
SELECT * FROM data
WHERE info.city = "London"
Notice that the STRUCT is named info and the data we accessed is city and used it in the WHERE clause.
Now, in order to delete the rows that contains an specific value within the STRUCT , in your case I assume it would be your_struct.column_1, you can use DELETE or MERGE and DELETE. I have saved the above data in a table to execute the below examples, which have the same output,
First method: DELETE
DELETE FROM `project.dataset.table`
WHERE info.city = "Sydney"
Second method: MERGE and DELETE
MERGE `project.dataset.table` a
USING (SELECT * from `project.dataset.table` WHERE info.city ="London") b
ON a.info.city =b.info.city
WHEN matched and b.id=1 then
Delete
And the output for both queries,
Row id info.name info.age info.city
1 1 Alex 30 NYC
2 1 Robert 25 Paris
3 1 Ralph 45 London
4 1 Mary 28 London
As you can see the row where info.city = "Sydney" was deleted in both cases.
It is important to point out that your data is excluded from your source table. Therefore, you should be careful.
Note: Since you want to run this process everyday, you could use Schedule Query within BigQuery Console, appending or overwriting the results after each run. Also, it is a good practice not deleting data from your source table. Thus, consider creating a new table from your source table without the rows you do not desire.
I have 3 tables out of which one is containing two foreign keys for the primary keys of the corresponding tables. Now I want to run a query which will fetch data from these tables and will store the result-set in a new table with only one primary key of each.
Tab 1: CUSTOMER (cust_id, col2, col3.....)
Tab 2: PRODUCT (prod-id, col2, col3.....)
Tab 3: SALES (s_id, cust_id,prod_id, col2, col3.....)
Now, I want result-set to be stored in a new table "RES" containing one 'cust_id', one 'prod_id' along with rest columns rather than those of two columns.
Please help.........
Thank you :)
I tried like this:
select *
into RES
from customer c
inner join sales s on s.cust_id = c.cust_id
where c.cust_id in (select cust_id
from sales
group by cust_id
having count(cust_id) >= 2)
order by s.cust_id;
but I get this error:
Msg 2705, Level 16, State 3, Line 1
Column names in each table must be unique. Column name 'cust_id' in table 'RES' is specified more than once.
Msg 2705, Level 16, State 3, Line 1 Column names in each table must be
unique. Column name 'cust_id' in table 'RES' is specified more than
once.
You are getting this error because you are inserting all the columns of tables customer and sales into RES, where custid column is present in both the tables.
Using * for insert is not a good practice. Instead of this you can change your query like following.
INSERT INTO RES(COL1,COL2.....COLN)
SELECT COL1, COL2, ........
FROM
<QUERY>
Even you are using SELECT .. INTO, in this case also you select explicit columns.
Suppose I have two tables :
Table 1:
SELECT * FROM ORIGINAL_DEALER;
ID ENAME
----- --------------------------
1 JOHN
2 00000JOHN
3 JACK
4 00000JACK
5 MARIA
6 00000MARIA
Table 2:
SELECT * FROM NAMES;
ID_NUM
------
1
2
3
4
5
6
I'll have to update NAMES.
Table ORIGINAL_DEALER has duplicate / invalid names such as
00000JOHN
The invalid names have exactly five zeros prefixed before the valid names. i.e valid name: JOHN
invalid name: 00000JOHN
Now I'll have to select the ID's of invalid names from
ORIGINAL_DEALER table and update the ID_NUM in NAMES table
WITH VALID ID. i.e when the invalid ename= '00000JOHN' has an ID
= 2 which is also invalid. but original_dealer is parent table, i'll have to replace all the invalid id's in NAMES table with valid
ones.
i.e the output after updating NAMES should be:
SELECT * FROM NAMES;
ID_NUM
------
1
1
3
3
5
5
How can I do it without manually setting it everytime as there will huge data?
I'm using Oracle DB
You can use a update statement with a lookup like below, by using an inner lookup subquery which in turn uses the TRIM function to strip out the leading zeroes during matching. We also restrict the update to just those Names linked to OriginalDealer records starting with 00000 to mimimize the risk of unnecessary updates.
UPDATE Names SET ID_NUM =
(SELECT od1.ID
FROM OriginalDealer od1
INNER JOIN OriginalDealer od2
ON od1.EName = TRIM(LEADING '0' FROM od2.EName)
WHERE od2.ID = Names.ID_NUM)
WHERE EXISTS
(
SELECT 1
FROM OriginalDealer od
WHERE od.ENAME LIKE '00000%'
AND Names.ID_NUM = od.ID
);
SqlFiddle here
Note that the model of marking duplicates with a prefix like '00000' is rather fragile:
It assumes there is exactly 1 match between the 00000NAME and NAME rows in ORIGINAL_DEALER
If the above isn't true, it will attempt to set Names.ID_NUM to NULL or Fail if there is more than one match (which is probably a good thing, we don't want to corrupt data)
I have a single table of activities, some labelled 'Assessment' (type_id of 50) and some 'Counselling' (type_id of 9) with dates of the activities. I need to compare these dates to find how long people wait for counselling after assessment. The table contains rows for many people, and that is the primary key of 'id'. My problem is how to produce a result row with both the assessment details and the counselling details for the same person, so that I can compare the dates. I've tried joining the table to itself, and tried nested subqueries, I just can't fathom it. I'm using Access 2010 btw.
Please forgive my stupidity, but here's an example of joining the table to itself that doesn't work, producing nothing (not surprising):
Table looks like:
ID TYPE_ID ACTIVITY_DATE_TIME
----------------------------------
1 9 20130411
1 v 50 v 20130511
2 9 20130511
3 9 20130511
In the above the last two rows have only had assessment so I want to ignore them, and just work on the situation where there's both assessment and counselling 'type-id'
SELECT
civicrm_activity.id, civicrm_activity.type_id,
civicrm_activity.activity_date_time,
civicrm_activity_1.type_id,
civicrm_activity_1.activity_date_time
FROM
civicrm_activity INNER JOIN civicrm_activity AS civicrm_activity_1
ON civicrm_activity.id = civicrm_activity_1.id
WHERE
civicrm_activity.type_id=9
AND civicrm_activity_1.type_id=50;
I'm actually wondering whether this is in fact not possible to do with SQL? I hope it is possible? Thank you for your patience!
Sounds to me like you only want to get the ID numbers where you have a TYPE_ID entry of both 9 and 50.
SELECT DISTINCT id FROM civicrm_activity WHERE type_id = '9' AND id IN (SELECT id FROM civicrm_activity WHERE type_id = '50');
This will give you a list of id's that has entries with both type_id 9 and 50. With that list you can now go and get the specifics.
Use this SQL for the time of type_id 9
SELECT activity_date_time FROM civicrm_activity WHERE id = 'id_from_last_sql' AND type_id = '9'
Use this SQL for the time of type_id 50
SELECT activity_date_time FROM civicrm_activity WHERE id = 'id_from_last_sql' AND type_id = '50'
Your query looks OK to me, too. The one problem might be that you use only one table alias. I don't know, but perhaps Access treats the table name "specially" such that, in effect, the WHERE clause says
WHERE
civicrm_activity.type_id=9
AND civicrm_activity.type_id=50;
That would certainly explain zero rows returned!
To fix that, use an alias for each table. I suggest shorter ones,
SELECT A.id, A.type_id, A.activity_date_time,
B.type_id, B.activity_date_time
FROM civicrm_activity as A
JOIN civicrm_activity as B
ON A.id = B.id
WHERE A.type_id=9
AND B.type_id=50;
Okay, I probably could have come up with a better title, but wasn't sure how to word it so let me explain.
Say I have a table with the column 'CODE'. Each record in my table will have either 'A', 'B', or 'C' as it's value in the 'CODE' column. What I would like is to get a count of how many 'A's, 'B's, and 'C's I have.
I know I could accomplish this with 3 different queries, but I'm wondering if there is a way to do it with just 1.
Use:
SELECT t.code,
COUNT(*) AS numInstances
FROM YOUR_TABLE t
GROUP BY t.code
The output will resemble:
code numInstances
--------------------
A 3
B 5
C 1
If a code exists that has not been used, it will not show up. You'd need to LEFT JOIN to the table containing the list of codes in order to see those that don't have any references.