BQ copy table command change column mode from REQUIRED to NULLABLE - sql

I ran below command to create a new table from an existing table. Problem is BQ creates this new table but change mode of REQUIRED columns to NULLABLE
create table `project_id.dataset.new_table_name` as
select * replace(
array(select as struct person.* except(add) from t.person) as person
)
from `project_id.dataset.table_name` t;
Expecting column mode unchanged.

The NOT NULL attribute of a table's column_schema does not propagate through queries over the table. If table T contains a column declared as x INT64 NOT NULL, for example, CREATE TABLE dataset.newtable AS SELECT x FROM T creates a table named dataset.newtable in which x is NULLABLE.
https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#column_name_and_column_schema
So, you'd better consider explicit schema definition.
CREATE OR REPLACE TABLE `project_id.dataset.table_name` (
Id STRING,
Person ARRAY<STRUCT<
Name STRING NOT NULL,
Add STRUCT<line STRING>
>>
) AS
SELECT 'id', [('John', STRUCT('aaa'))];
CREATE OR REPLACE TABLE `project_id.dataset.new_table_name` (
Id STRING,
Person ARRAY<STRUCT<
Name STRING NOT NULL
>>
) AS
SELECT * REPLACE (ARRAY(SELECT AS STRUCT Person.* EXCEPT(add) FROM t.Person) AS Person)
FROM `project_id.dataset.table_name` t;

Related

INSERTING VALUES INTO A NESTED TABLE

I am trying to develop a university database with the help of nested tables, I have successfully created all other nested tables required and inserted data as well, but while inserting data into marks table I am facing problem of inconsistent datatype.
codes:
CREATE OR REPLACE TYPE MODULE_MARKS;
CREATE OR REPLACE TYPE MM_NT_TYPE AS TABLE OF REF MODULE_MARKS;
CREATE OR REPLACE TYPE MODULE_MARKS AS OBJECT
(
MODULE REF MODULE_T, MARKS_OBTAINED, TOTAL_MARKS, STATUS
)
CREATE TABLE MARK_TAB
(
student ref student_t,
modules_marks mm_nt_type
)
I am able to insert reference to student correctly but I want to insert data into module_marks.
Tried doing :
INSERT INTO MARK_TAB VALUES((SELECT REF(S) FROM STUDENT_TAB S WHERE
S.S_ID=1),
MM_NT_TYPE( MODULE_MARKS_T((SELECT REF (M) FROM MODULE_TAB M WHERE
M.MODULE_ID =1),
90,100,'PASS')));
this shoes the error ORA-00932. EXPECTED REFERENCE OF MODULE_MARKS_T GOT MODULE_MARKS_T.
It seems a familiar structure to me. Maybe I have created the same structure for one of my projects.
I think you are confused when inserting the record in the column having a type which is table of REF.
I have COURSES_TABLE_TYPE which is table of REF COURSES_T and courses table is table of COURSES_T;
I suggest you do the following:
INSERT INTO DEPARTMENT VALUES (
1,
COURSES_TABLE_TYPE(( -- REFs of single records delimited by comma
SELECT
REF(C)
FROM
COURSE C
WHERE
COURSE_ID = 1
),(
SELECT
REF(C)
FROM
COURSE C
WHERE
COURSE_ID = 2
))
);
MM_NT_TYPE is a collection of REF MODULE_MARKS whereas you are passing MODULE_MARKS objects and not references. Instead, you need to have a table containing containing MODULE_MARKS objects that you can reference:
CREATE TABLE module_marks_tab OF module_marks;
Then you can reference those objects. For example:
INSERT INTO mark_tab VALUES (
( SELECT REF(s) FROM students s WHERE id = 2 ),
MM_NT_TYPE(
( SELECT REF( m ) FROM module_marks_tab m WHERE m.module.id = 1 AND marks_obtained = 3 ),
( SELECT REF( m ) FROM module_marks_tab m WHERE m.module.id = 3 AND marks_obtained = 8 )
)
);
db<>fiddle

sql conversion script

I have a 2 databases that I want to merge with some similiar tables. The source tables have id as bigint and my destination table has int as ID. There aren't that many records in my source table (< 20k) so I want to assign new ids to all records so the ids can fit in an int. How can I do this with sql?
First Option
You can Use Sequence object as follow:
First Create a Sequence object and assign it's Start With value to max Id value in destination table plus 1. For example if max Id in destination table is 100, you need to assign 101 as Start With. You can also obtain the max Id value from destination table using a Max(Id) aggregate function and store it in a variable:
CREATE SEQUENCE SeqId
START WITH [Max value of Id in destination table]
INCREMENT BY 1 ;
GO
Then insert to destination table using following query:
Insert Into tblXXX (Id, ...) Values (NEXT VALUE FOR SeqId, ...)
Read more about Sequence Object
Second Option
You can make the destination table's Id column as Identity column with seed equal to destination table's Id column max value and Increment equal to 1.
Here is detailed example also Here
You did not provide much details so I can only provide a general guideline:
Note: Example assumes that you want to merge tables A and B into C and you want to generate new IDs. I also assume that these IDs are not referenced by other tables (foreign keys).
First you get record counts from tables A and B:
DECLARE #countA INT
DECLARE #countB INT
SET #countA = ( SELECT COUNT(*) FROM A )
SET #countB = ( SELECT COUNT(*) FROM B )
Next you use a window function to generate new IDs and insert records into table C.
INSERT INTO C
SELECT #countA + ROW_NUMBER() OVER( ORDER BY ID ) AS ID, ....
FROM A
INSERT INTO C
SELECT #countA + #countB + ROW_NUMBER() OVER( ORDER BY ID ) AS ID, ....
FROM B

How to join on to variable table based on field value?

I have a table called tracks with with basic data. The important parts are that this table has a column named id and event. The value of the event field is the name of another table. That table has a matching id with a lot of details about what was tracked. Is it possible to do something like this?
SELECT id, event, e.*
FROM tracks t
LEFT JOIN $event e ON t.id = e.id
The value of event could be one of a hundred different values.
Probably you could use inheritance: PostgreSQL 9.5.1 Documentation: Inheritance
In this case you may have empty parent table:
CREATE TABLE events(
id SERIAL PRIMARY KEY,
event_type INTEGER,
...
);
And bunch of children tables
CREATE TABLE events_1(event_type INTEGER DEFAULT 1 CHECK(event_type = 1)) INHERITS (events);
CREATE TABLE events_2(event_type INTEGER DEFAULT 2 CHECK(event_type = 2)) INHERITS (events);
...
Then you will be able to use queries like:
SELECT t.id, t.event_type, e.*
FROM tracks t
JOIN events e on t.id = e.id AND t.event_type = e.event_type;
But you have to add all columns to parent if you want request them from parent table.
UPD: you cannot use variable as table name in pure sql. Only way to do it is dynamic code generation (for example in plpgsql).
You can't have a variable table name in a simple SQL query; one way or another, you need to build and execute a dynamic query string.
If you want to avoid doing this in your application code, you'll need to use PL/pgSQL's EXECUTE, which means you'll have to create a function to do it.
If the common fields in your event tables are e.g. x INT, y INT, then this should work (though it may not be particularly efficient):
CREATE FUNCTION EventTable(TableName TEXT, id INT) RETURNS TABLE (x INT, y INT) AS
$$
BEGIN
RETURN QUERY EXECUTE 'SELECT x, y FROM ' || TableName || ' WHERE id = $1' USING id;
END
$$
LANGUAGE plpgsql;
SELECT id, event, e.*
FROM t
LEFT JOIN EventTable(event, id) e ON true;
SQLFiddle example

How to get an attribute value from a table to use in a query?

I have a table to store table names (lets call it "CUSTOM_TABLES").
I have a table to register data tracking (call it "CONSUMPTIONS").
I have tables that the user was created and I don't know its names (created at runtime) so, the system create the table ( execute the DDL) and store its name in "CUSTOM_TABLES". Lets call it "USER_TABLE" for now.
When a data is produced in a table "USER_TABLE", I register in the tracking table ("CONSUMPTIONS") the row ID of data and the ID of the "USER_TABLE" found in "CUSTOM_TABLES".
Now, I need to find, given a consumption, what table and what row the data is. Remember: in "CONSUMPTIONS" table I have only an ID (FK) pointing to "CUSTOM_TABLES".
CREATE TABLE consumptions
(
id_consumption serial NOT NULL,
id_row integer,
id_table integer
)
CREATE TABLE custom_tables
(
id_table serial NOT NULL,
description character varying(250),
name character varying(150)
)
The query I need is here:
select * from consumptions c
join insurance i on c.id_row = i.index_id
In this case, "insurance" is the "USER_TABLE".
But I don't know "insurance". I need to find it in "CUSTOM_TABLES" by using its ID.
select name from custom_tables where
id_table = consumption.id_table
The final query must be something like:
select * from consumptions c
join
(select name from custom_tables t where t.id_table = c.id_table) i
on c.id_row = i.index_id
I guarantee the "user_tables" have "index_id" attribute as its PK.
I prefer do not use functions.

SQL - How to select subelements from type

I am trying to select subelements from a self-defined type in SQL.
CREATE TYPE Name AS (
surname VARCHAR,
givenName VARCHAR );
CREATE TABLE test (A Name );
INSERT INTO test VALUES ( ('ter', 'minator') );
How is it possible to give me all results matching test.surname = 'ter'?
The following does not work:
SELECT * FROM test WHERE a.surname = 'ter';
See:
http://www.postgresql.org/docs/current/static/rowtypes.html
For example, you might try to select some subfields from our on_hand example table with something like:
SELECT item.name FROM on_hand WHERE item.price > 9.99;
This will not work since the name item is taken to be a table name, not a column name of on_hand, per SQL syntax rules. You must write it like this:
SELECT (item).name FROM on_hand WHERE (item).price > 9.99;
or if you need to use the table name as well (for instance in a multitable query), like this:
SELECT (on_hand.item).name FROM on_hand WHERE (on_hand.item).price > 9.99;