Select query question - sql

I have a table in a mysql-database with the fields
"name", "title", "cd_id", "tracks"
The entries look like this:
Schubert | Symphonie Nr 7 | 27 | 2
Brahms | Symphonie Nr 1 | 11 | 4
Brahms | Symphonie Nr 2 | 27 | 4
Shostakovich | Jazz Suite Nr 1 | 19 | 3
To get the tracks per cd (cd_id) I have written this script:
#!/usr/bin/env perl
use warnings; use strict;
use DBI;
my $dbh = DBI->connect(
"DBI:mysql:database=my_db;",
'user', 'passwd', { RaiseError => 1 } );
my $select_query = "SELECT cd_id, tracks FROM my_table";
my $sth = $dbh->prepare( $select_query );
$sth->execute;
my %hash;
while ( my $row = $sth->fetchrow_hashref ) {
$hash{"$row->{cd_id}"} += $row->{tracks};
}
print "$_ : $hash{$_}\n" for sort { $a <=> $b } keys %hash;
Is it possible to get these results directly with an appropriate select query?

"SELECT cd_id, SUM(tracks) as tracks FROM my_table GROUP BY cd_id"
edit: see here for further information and other things you can do with GROUP BY: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html

Use an aggregate function
SELECT `cd_id`, SUM(`tracks`) AS s
FROM `your_table`
GROUP BY `cd_id`

Related

How can I extract a json column into new columns automatically in Snowflake SQL?

This is as example taken from another thread, but essentially I would like to achieve this:
Sample data
ID Name Value
1 TV1 {"URL": "www.url.com", "Icon": "some_icon"}
2 TV2 {"URL": "www.url.com", "Icon": "some_icon", "Facebook": "Facebook_URL"}
3 TV3 {"URL": "www.url.com", "Icon": "some_icon", "Twitter": "Twitter_URL"}
..........
Expected output
ID Name URL Icon Facebook Twitter
1 TV1 www.url.com some_icon NULL NULL
2 TV2 www.url.com some_icon Facebook_URL NULL
3 TV3 www.url.com some_icon NULL Twitter_URL
I'm totally new to Snowflake so I'm shaking my head on how to do this easily (and hopefully automatically, in the case where some rows might have more elements in the json than other rows, which would be tedious to assign manually). Some lines might have sub-categories too.
I found the parse_json function for Snowflake, but it's only giving me the same json column in a new column, still in json format.
TIA!
You can create a view over your table with the following SELECT:
SELECT ID,
Name,
Value:URL::varchar as URL,
Value:Icon::varchar as Icon,
Value:Facebook::varchar as Facebook,
Value:Twitter::varchar as Twitter
FROM tablename;
Additional attributes will be ignored unless you add them to the view. There is no way to "automatically" include them into the view, but you could create a stored procedure that dynamically generates the view based on all the attributes that are in the full variant content of a table.
You can create a SP to automatically build the CREATE VIEW for you based on the JSON data in the VARIANT.
I have some simple example below:
-- prepare the table and data
create or replace table test (
col1 int, col2 string,
data1 variant, data2 variant
);
insert into test select 1,2, parse_json(
'{"URL": "test", "Icon": "test1", "Facebook": "http://www.facebook.com"}'
), parse_json(
'{"k1": "test", "k2": "test1", "k3": "http://www.facebook.com"}'
);
insert into test select 3,4,parse_json(
'{"URL": "test", "Icon": "test1", "Twitter": "http://www.twitter.com"}'
), parse_json(
'{"k4": "v4", "k3": "http://www.ericlin.me"}'
);
-- create the SP, we need to know which table and
-- column has the variant data
create or replace procedure create_view(
table_name varchar
)
returns string
language javascript
as
$$
var final_columns = [];
// first, find out the columns
var query = `SHOW COLUMNS IN TABLE ${TABLE_NAME}`;
var stmt = snowflake.createStatement({sqlText: query});
var result = stmt.execute();
var variant_columns = [];
while (result.next()) {
var col_name = result.getColumnValue(3);
var data_type = JSON.parse(result.getColumnValue(4));
// just use it if it is not a VARIANT type
// if it is variant type, we need to remember this column
// and then run query against it later
if (data_type["type"] != "VARIANT") {
final_columns.push(col_name);
} else {
variant_columns.push(col_name);
}
}
var columns = {};
query = `SELECT ` + variant_columns.join(', ') + ` FROM ${TABLE_NAME}`;
stmt = snowflake.createStatement({sqlText: query});
result = stmt.execute();
while (result.next()) {
for(i=1; i<=variant_columns.length; i++) {
var sub_result = result.getColumnValue(i);
if(!sub_result) {
continue;
}
var keys = Object.keys(sub_result);
for(j=0; j<keys.length; j++) {
columns[variant_columns[i-1] + ":" + keys[j]] = keys[j];
}
}
}
for(path in columns) {
final_columns.push(path + "::STRING AS " + columns[path]);
}
var create_view_sql = "CREATE OR REPLACE VIEW " +
TABLE_NAME + "_VIEW\n" +
"AS SELECT " + "\n" +
" " + final_columns.join(",\n ") + "\n" +
"FROM " + TABLE_NAME + ";";
snowflake.execute({sqlText: create_view_sql});
return create_view_sql + "\n\nVIEW created successfully.";
$$;
Execute the SP will return below string:
call create_view('TEST');
+---------------------------------------+
| CREATE_VIEW |
|---------------------------------------|
| CREATE OR REPLACE VIEW TEST_VIEW |
| AS SELECT |
| COL1, |
| COL2, |
| DATA1:Facebook::STRING AS Facebook, |
| DATA1:Icon::STRING AS Icon, |
| DATA1:URL::STRING AS URL, |
| DATA2:k1::STRING AS k1, |
| DATA2:k2::STRING AS k2, |
| DATA2:k3::STRING AS k3, |
| DATA1:Twitter::STRING AS Twitter, |
| DATA2:k4::STRING AS k4 |
| FROM TEST; |
| |
| VIEW created successfully. |
+---------------------------------------+
Then query the VIEW:
SELECT * FROM TEST_VIEW;
+------+------+-------------------------+-------+------+------+-------+-------------------------+------------------------+------+
| COL1 | COL2 | FACEBOOK | ICON | URL | K1 | K2 | K3 | TWITTER | K4 |
|------+------+-------------------------+-------+------+------+-------+-------------------------+------------------------+------|
| 1 | 2 | http://www.facebook.com | test1 | test | test | test1 | http://www.facebook.com | NULL | NULL |
| 3 | 4 | NULL | test1 | test | NULL | NULL | http://www.ericlin.me | http://www.twitter.com | v4 |
+------+------+-------------------------+-------+------+------+-------+-------------------------+------------------------+------+
Query the source table:
SELECT * FROM TEST;
+------+------+------------------------------------------+-----------------------------------+
| COL1 | COL2 | DATA1 | DATA2 |
|------+------+------------------------------------------+-----------------------------------|
| 1 | 2 | { | { |
| | | "Facebook": "http://www.facebook.com", | "k1": "test", |
| | | "Icon": "test1", | "k2": "test1", |
| | | "URL": "test" | "k3": "http://www.facebook.com" |
| | | } | } |
| 3 | 4 | { | { |
| | | "Icon": "test1", | "k3": "http://www.ericlin.me", |
| | | "Twitter": "http://www.twitter.com", | "k4": "v4" |
| | | "URL": "test" | } |
| | | } | |
+------+------+------------------------------------------+-----------------------------------+
You can refine this SP to detect nested data and have them added to the columns list as well.

How to automate a field mapping using a table in snowflake

I have one column table in my snowflake database that contain a JSON mapping structure as following
ColumnMappings : {"Field Mapping": "blank=Blank,E=East,N=North,"}
How to write a query that if I feed the Field Mapping a value of E I will get East or if the value if N I will get North so on and so forth without hard coding the value in the query like what CASE statement provides.
You really want your mapping in this JSON form:
{
"blank" : "Blank",
"E" : "East",
"N" : "North"
}
You can achieve that in Snowflake e.g. with a simple JS UDF:
create or replace table x(cm variant) as
select parse_json(*) from values('{"fm": "blank=Blank,E=East,N=North,"}');
create or replace function mysplit(s string)
returns variant
language javascript
as $$
res = S
.split(",")
.reduce(
(acc,val) => {
var vals = val.split("=");
acc[vals[0]] = vals[1];
return acc;
},
{});
return res;
$$;
select cm:fm, mysplit(cm:fm) from x;
-------------------------------+--------------------+
CM:FM | MYSPLIT(CM:FM) |
-------------------------------+--------------------+
"blank=Blank,E=East,N=North," | { |
| "E": "East", |
| "N": "North", |
| "blank": "Blank" |
| } |
-------------------------------+--------------------+
And then you can simply extract values by key with GET, e.g.
select cm:fm, get(mysplit(cm:fm), 'E') from x;
-------------------------------+--------------------------+
CM:FM | GET(MYSPLIT(CM:FM), 'E') |
-------------------------------+--------------------------+
"blank=Blank,E=East,N=North," | "East" |
-------------------------------+--------------------------+
For performance, you might want to make sure you call mysplit only once per value in your mapping table, or even pre-materialize it.

Codeigniter merge into one row record with sql one to many relationship

I would like to query one to many relationship
eg:
Table A: Table B:
id | country_name id | name | table_A_id
1 | Usa 1 | kerry | 1
2 | Australia 2 | jerry | 1
3 | tommy | 2
4 | cherry | 2
my purpose is to query the result to merge one row record
eg: result list:
1 Record 2 Record
Usa Australia
kerry tommy
jeryy cherry
Currently, I am using Codeignter framework and beginner for sql, please don't mind me guys.
$this->db->select('*')
>from("table A")
->join("table B", "table.A.id = table_A_id");
$query = $this->db->get();
if($query->num_rows() > 0) {
return $query->result();
}
My view
<?php foreach($posts as $post) {
echo $post->country_name;
echo $post->name;
} ?>
However, it gives me 4 row records.
1 Record 2 Record
Usa Usa
kerry jerry
3 Record 4 Record
Australia Australia
tommy cherry
Thanks you guys in advance for helping me.
Here is what you missed
$this->db->select('*')
>from("table A")
->join("table B", "table.A.id = table_A_id");
$query = $this->db->get();
if($query->num_rows() > 0) {
return $query->result();
}
it should have been
->join("table B", 'B.table_A_id=A.id');
hope this make sense
option B
$this->db->join('table B','B.table_A_id=A.id','INNER');
$query = $this->db->get('table A');
Try out this
SELECT country, group_concat(name) FROM city c INNER JOIN employee e
ON c.id = e.city_id group by c.id
output
('USA ' , 'jerry ,kerry'),
('Australia', 'cherry ,tommy');
My solution found without using inner join:
public function mergeRecord() {
$array_store = array();
foreach($this->one() as $row) {
$child = $this->many($row['id']);
if($child) {
$return = array_merge(array("parent" => $row), array("child" =>$child));
array_push($array_store, $return);
}
}
return $array_store;
}
public function one() {
$query = $this->db->select("*")
->get("table A")
->result_array();
return $query;
}
public function many($id) {
$query = $this->db->select("id, name")
->where("table_A_id", $id)
->get("table B")
->result_array();
return $query;
}

Simple relation in YII, can't get it done

This is the first time I use relations in Yii, the question is very simple.
MODULE TABLE
name - PK
status - FK status_id
STATUS TABLE
id PK
name
So, each Module HAS one status.
But I can not seem to get it working.
Module.php (Model)
public function relations()
{
return array(
'status'=>array(self::BELONGS_TO, 'ModuleStatus', 'status'),
);
}
I access them this way:
$modulesAR = Module::model()->with('status')->findAll();
if( $modulesAR )
{
foreach( $modulesAR as $moduleAR )
{
$this->modules[ $moduleAR->name ] = array(
'sessionLimit' => isset($moduleAR->sessionLimit) ? $moduleAR->sessionLimit : 0,
'status' => isset($moduleAR->status) ? $moduleAR->status : 'disabled',
);
}
}
Var_dump(Yii::app()->module->modules;
array(3) {
["digidoc"]=>
array(2) {
["sessionLimit"]=>
int(0)
["status"]=>
string(1) "2" // Should say "Disabled"
}
["docusearch"]=>
array(2) {
["sessionLimit"]=>
int(0)
["status"]=>
string(1) "1" // Should say "Enabled"
}
["printbox"]=>
array(2) {
["sessionLimit"]=>
int(0)
["status"]=>
string(1) "2" // Should say "Disabled"
}
}
I will appreciate any help.
Thanks!
EDIT:
query executed by Yii:
SELECT `t`.`name` AS `t0_c0`, `t`.`status_id` AS `t0_c1`, `t`.`session_limit` AS `t0_c2`, `status`.`id` AS `t1_c0`, `status`.`name` AS `t1_c1` FROM `ss_module` `t` LEFT OUTER JOIN `ss_module_status` `status` ON (`status`.`id`=`t`.`name`)
+------------+-------+-------+-------+-------+
| t0_c0 | t0_c1 | t0_c2 | t1_c0 | t1_c1 |
+------------+-------+-------+-------+-------+
| digidoc | 2 | 0 | NULL | NULL |
| docusearch | 1 | 2 | NULL | NULL |
| printbox | 2 | 0 | NULL | NULL |
+------------+-------+-------+-------+-------+
EDIT 2:
Changing the last
ON (`status`.`id`=`t`.`name`);
to
ON (`status`.`id`=`t`.`status_id`);
works as expected, by I dont know how to fix it in Yii.
When you peek into $moduleAR->status, you get the whole Status model instance, not it's id. So just go on and peek into that instance:
$moduleAR->status->name.
That's what ActiveRecord magic is intended for ;)
And inversely, if you have Status model, you should have a relation there saying something like this:
'modules' => array(self::HAS_MANY, 'Module', 'status_id')
Which could make the following possible:
$status = Status::model()->find(/*somehow*/);
/* now that $status is an Status instance */
foreach ($status->modules as $module) {
// here you are! looping over all modules connected to this status
// each $module is a full-fledged Module instance
}

yii - create nested cgridview

Is it possible to create nested table using cgridview?
I'd like to have the final output as follows
| Transaction | Total |
| T-001 | $100 |
| Item | Price | // here is the nested table
| I-1 | $50 |
| I-2 | $50 |
| T-002 | $90 |
| Item | Price | // here is the nested table
| I-3 | $90 |
I know you can do this using a custom template, but i'd like a neater solution using a widget like CGRidView.
Thanks
If nested table is inside cell then you can, just create function in your model that will render table and return content. You can set third parameter of widget function to true to return content. If you want to enable pagination for nested table then be sure to manually set widget id and allow ajax update.
In model:
function getNestedTable() {
return Yii::app()->controller->widget(..., ..., true);
}
In columns definition use:
'columns' => array(
array(
'name' => 'nestedTable',
'type' => 'raw'
)
)
I think the best way to achieve what you want is to use custom functions in your CActiveRecord model (if you've a CActiveDataprovider for the grid), and put that 'nested table' as normal column:
| Transaction | Item | Price | Total |
------------------------------------------
| T-001 | I-1 | $50 | $100 |
| I-2 | $50 |
------------------------------------------
| T-002 | I-3 | $90 | $90 |
------------------------------------------
In your model you've to define get functions that return data in HTML with line breaks (for example with a br:
class Item extends CActiveRecord {
...
public function getIdItems()
{
$string = '';
foreach($this->items as $item) {
if ($string != '') $string .= '<br/>';
$string .= ' '.$item->textId; // 'I-3', 'I-2'...
}
return $string;
}
public function getPriceItems()
{
$string = '';
foreach($this->items as $item) {
if ($string != '') $string .= '<br/>';
$string .= ' '.$item->price; // $50, $90...
}
return $string;
}
...
}
And to show the new columns in your grid:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'anotacionesGrid',
'dataProvider'=>$dataProvider,
'columns'=>array(
'transaction',
'idItems:html:Item',
'priceItems:html:Price',
'total'
)
);