I have table like below
table
CREATE TABLE IF NOT EXISTS "Article"(
"ArticleId" SERIAL NOT NULL,
"GenresIdList" integer[],
...
PRIMARY KEY ("ArticleId")
);
CREATE TABLE IF NOT EXISTS "Tag0"(
"TagId" SERIAL NOT NULL,
"Name" varchar,
...
PRIMARY KEY ("TagId")
);
ArticleId | GenresIdList
1 | {1} |
2 | {1} |
3 | {1,2} |
4 | {1,2,3} |
TagId | Name
1 | hiphop
2 | rock
When user input data inputGenres I want get below result:
if inputGenres = ['hiphop','rock','classical']; then will get no rows in Article
if inputGenres = ['hiphop','rock']; get Article rows 3 and 4
but because I select two table separate then even I use && in select article table when inputGenres = ['hiphop','rock','classical']; when convert to id array I will become [1,2] because there is no classical, then I will get rows 3 and 4.
How to solve this?
ps. I have to design table like this, only store id not store name in 'Article'. so I hope not redesign table
code (with nodejs)
// convert inputGenres to tag0TagIdList
var tag0TagIdList = [];
var db = dbClient;
var query = 'SELECT * FROM "Tag0" WHERE "Name" IN (';
for (var i = 0; i < inputGenres.length; i++) {
if (i > 0) {
query += ',';
}
query += '$' + (i + 1);
}
query += ') ORDER BY "Name" ASC';
var params = inputGenres;
var selectTag0 = yield crudDatabase(db,query,params);
for (var i = 0; i < selectTag0.result.rows.length; i++) {
tag0TagIdList.push(selectTag0.result.rows[i].TagId);
}
// end: convert inputGenres to tag0TagIdList
var db = dbClient;
var query = 'SELECT * FROM "Article" WHERE "GenresIdList" && $1';
var params = [tag0TagIdList];
var selectArticle = yield crudDatabase(db,query,params);
var tag0TagIdList = [];
var db = dbClient;
var query = 'select * from "Article" where "GenresIdList" #> (select array_agg ("TagId") from unnest (array[';
for (var i = 0; i < inputGenres.length; i++) {
if (i > 0) {
query += ',';
}
query += '$' + (i + 1);
}
query += ']) as input_tags left join "Tag0" on ( "Name" = input_tags))';
I don't know java much, but this should return what you want.
query example:
SELECT * FROM "Article"
WHERE
"GenresIdList" #> (
SELECT
array_agg ( "TagId" )
FROM
unnest (ARRAY [ 'hiphop', 'rock' ] ) AS input_tags
LEFT JOIN "Tag0" ON (
"Name" = input_tags ) )
Related
I am trying to creae a list of json object by processing the result set from a different query execution. The liast of json I want to persist to another table and copy the json to a stage so that later i can run the copy command and json data gets copied to the other table. How can I acheive this? Any thoughts.? The code is shared as screen shot in the image attached.
Code
In case you are only looking to persist/insert the resultset getting generated within your procedure then you can try to incorporate something like following in your code -
CREATE OR REPLACE procedure stproc1(anyvar varchar)
RETURNS varchar not null
LANGUAGE javascript strict
AS
$$
var rowarr = [];
var rowobj = {};
// BUILDING A DUMMY ARRAY
rowobj['processid'] = 100;
rowobj['loc'] = 200;
rowobj['item'] = 300;
rowarr.push(rowobj);
// ENSURE TO nullify object BEFORE ANOTHER PUSH
rowobj={};
rowobj['processid'] = 10;
rowobj['loc'] = 20;
rowobj['item'] = 30;
rowarr.push(rowobj);
len = rowarr.length;
var querystr = "";
var ret_str = "";
//Iterate though ARRAY to INSERT into target TABLE
for (var x = 0; x < len; x++) {
querystr = "insert into item_j values (?)";
var statement = snowflake.createStatement({sqlText:querystr, binds:[JSON.stringify(rowarr[x])] });
var rs = statement.execute();
}
return "BATCH";
$$
;
After executing above result will be stored in table ITEM_J that has only one column of type VARCHAR.
select * from item_j;
+-------+
| T_VAL |
|-------|
+-------+
call stproc1('a');
+---------+
| STPROC1 |
|---------|
| BATCH |
+---------+
select * from item_j;
+----------------------------------------+
| T_VAL |
|----------------------------------------|
| {"processid":100,"loc":200,"item":300} |
| {"processid":10,"loc":20,"item":30} |
+----------------------------------------+
2 Row(s) produced.
I need to fetch all Primary Keys, their parent Table Name , Column Name, and Schema Name together.
I am using INFORMATION_SCHEMA for all metadata fetching, SHOW PRIMARY KEYS/DESCRIBE TABLE does the job but it's not an option here.
Need something similar to SELECT *FROM DB.INFORMATION_SCHEMA.XXX.
What are the options we have here?
*I am Using JDBC
You may consider using: getPrimaryKeys(String, String, String)
Details: https://docs.snowflake.com/en/user-guide/jdbc-api.html#object-databasemetadata
A while back, I wrote a user defined table function (UDTF) to get the PK column(s) for a single table, each column in the PK as a single row in the return. I extended it to return a table with all the columns in PKs in an entire database.
Once you create the UDTF, you can get all the PKs for a database like this:
select * from table(get_pk_columns(get_ddl('database', 'MY_DB_NAME')));
It will return a table with columns for the schema name, table name, and column name(s). Note that if there's a composite PK, it shows in the table as one row per column. You can of course use an aggregate function such as listagg() to change that into a single row with the columns names of the composite PK separated by commas.
It's possible that if you have a very large number of tables/columns in your database, the return of the GET_DDL() function will be too large to fit into the 16mb limit. If it does fit, this should return the results quickly.
/********************************************************************************************************
* *
* User defined table function (UDTF) to get all primary keys for a database. *
* *
* #param {string}: DATABASE_DDL The DDL for the database to get the PKs. Usually use GET_DDL() *
* #return {table}: A table with the columns comprising the table's primary key *
* *
********************************************************************************************************/
create or replace function GET_PK_COLUMNS(DATABASE_DDL string)
returns table ("SCHEMA_NAME" string, "TABLE_NAME" string, PK_COLUMN string)
language javascript
as
$$
{
processRow: function get_params(row, rowWriter, context){
var startTableLine = -1;
var endTableLine = -1;
var dbDDL = row.DATABASE_DDL.replace(/'[\s\S]*'/gm, '')
var lines = dbDDL.split("\n");
var currentSchema = "";
var currentTable = "";
var ln = 0;
var tableDDL = "";
var pkCols = null;
var c = 0;
for (var i=0; i < lines.length; i++) {
if (lines[i].match(/^create .* schema /)) {
currentSchema = lines[i].split("schema")[1].replace(/;/, '');
//rowWriter.writeRow({PK_COLUMN: "currentSchema = " + currentSchema});
}
if (lines[i].match(/^create or replace TABLE /)) {
startTableLine = i;
}
if (startTableLine != -1 && lines[i] == ");") {
endTableLine = i;
}
if (startTableLine != -1 && endTableLine != -1) {
// We found a table. Now, join it and send it for parsing
tableDDL = "";
for (ln = startTableLine; ln <= endTableLine; ln++) {
if (ln > 0) tableDDL += "\n";
tableDDL += lines[ln];
}
startTableLine = -1;
endTableLine = -1;
currentTable = getTableName(tableDDL);
pkCols = getPKs(tableDDL);
for (c = 0; c < pkCols.length; c++) {
rowWriter.writeRow({PK_COLUMN: pkCols[c], SCHEMA_NAME: currentSchema, TABLE_NAME: currentTable});
}
}
}
function getTableName(tableDDL) {
var lines = tableDDL.split("\n");
var s = lines[1];
s = s.substring(s.indexOf(" TABLE ") + " TABLE ".length);
s = s.split(" (")[0];
return s;
}
function getPKs(tableDDL) {
var c;
var keyword = "primary key";
var ins = -1;
var s = tableDDL.split("\n");
for (var i = 0; i < s.length; i++) {
ins = s[i].indexOf(keyword);
if (ins != -1) {
var colList = s[i].substring(ins + keyword.length);
colList = colList.replace("(", "");
colList = colList.replace(")", "");
var colArray = colList.split(",");
for (pkc = 0; c < colArray.length; pkc++) {
colArray[pkc] = colArray[pkc].trim();
}
return colArray;
}
}
return []; // No PK
}
}
}
$$;
I did this using a very simple SQL based UDTF:
CREATE OR REPLACE FUNCTION admin.get_primary_key(p_table_nm VARCHAR)
RETURNS TABLE(column_name VARCHAR, ordinal_position int)
AS
WITH t AS (select get_ddl('TABLE', p_table_nm) tbl_ddl)
, t1 AS (
SELECT POSITION('primary key (', tbl_ddl) + 13 pos
, SUBSTR(tbl_ddl, pos, POSITION(')', tbl_ddl, pos) - pos ) str
FROM t
)
SELECT x.value column_name
, x.index ordinal_position
FROM t1
, LATERAL SPLIT_TO_TABLE(t1.str, ',') x
;
You can then query this in a SQL statement:
select *
FROM TABLE(admin.get_primary_key('<your table name>'));
Unfortunately, due to the odd implementation of GET_DDL(), it will only accept a string literal and you can't use this function with a lateral join to information_schema.tables. Get the following error:
SQL compilation error: Invalid value [CORRELATION(T.TABLE_SCHEMA) ||
'.' || CORRELATION(T.TABLE_NAME)] for function '2', parameter
EXPORT_DDL: constant arguments expected
select count(tblVV.VNme) as total,
tblvV.VNme
from tblVV
inner join tblRV
on tblVV.MID=tblRV.ID
inner join tblRe
on tblRV.RID=tblRe.RID
where tblRe.StartDate>= '2016-07-01 00:00:00' and
tblRe.EndDate<= '2016-07-31 23:59:59' and
tblRe.Reg= 'uk' and
tblRV.RegNo='BR72' and
tblVV.VNme <>''
group by tblVV.VNme
For the above query I get:
total Vame
1 DDSB
11 MV
The above SQL query shows me correct data so now i try to convert above query to linq query
[WebMethod]
public static string GetVo(string RegNo)
{
string data = "[";
try
{
Ts1 DB = new Ts1();
var re = (from vehvoila in DB.tblVV
join regveh in DB.tblRV on vehvoila.MID equals regveh.ID
join reg in DB.tblReg on regveh.RID equals reg.RID
where regveh.RegNo == RegNo &&
vehvoila.Vame != ""
group vehvoila by vehvoila.Vame into g
select new
{
VNme = g.Key,
cnt = g.Select(t => t.Vame).Count()
}).ToList();
if (re.Any())
{
data += re.ToList().Select(x => "['" + x.Vame + "'," + x.cnt + "]")
.Aggregate((a, b) => a + "," + b);
}
data += "]";
}
linq query show me return data like this
[['DDSB',1],['DPSB',1],['DSB',109],['MV',39],['PSB',1]]
Whereas I want data this
[['DDSB',1],['MV',11]]
Now the data which return SQL query is correct so how I correct linq query
Note: forget fromdate,todate,region parameter in SQL query . because I have page in which I put dropdown and fromdate and todate picker and there is button so when I select values i.e. UK, and dates then data is display in table then when I click on any row in table then I want to get this data in data +=”]”;
actually above linq query work behind clicking on row
total Vame
1 DDSB
11 MV
You can write it all like this:
Ts1 db = new Ts1();
var result = (from vehvoila in db.tblVV
join regveh in db.tblRV on vehvoila.MID equals regveh.ID
join reg in db.tblReg on regveh.RID equals reg.RID
where reg.StartDate >= new DateTime(2016, 7, 1) &&
reg.EndDate < new DateTime(2016, 8, 1) &&
reg.Reg == "uk" &&
regveh == "BR72" &&
vehvoila != ""
group vehvoila by vehvoila.Vame into g
select $"[{g.Key},{g.Count()}]");
var data = $"[{string.Join(",", result)}]";
Because you only use the result for the creation of the string in the select I just return the string formatted for a single item and then later used string.Join instead of using the .Aggregate - I think a bit cleaner
The $"{}" syntax is the C# 6.0 string interpolation
In the condition of the EndDate I decided to use < instead of the <= with the change of the date - At least in oracle when you partition the table by date it is better for performance - maybe also in sql server
Without string interpolation:
Ts1 db = new Ts1();
var result = (from vehvoila in db.tblVV
join regveh in db.tblRV on vehvoila.MID equals regveh.ID
join reg in db.tblReg on regveh.RID equals reg.RID
where reg.StartDate >= new DateTime(2016, 7, 1) &&
reg.EndDate < new DateTime(2016, 8, 1) &&
reg.Reg == "uk" &&
regveh == "BR72" &&
vehvoila != ""
group vehvoila by vehvoila.Vame into g
select new { Key = g.Key, Count = g.Count()})
.AsEnumerable()
.Select(g => string.Format("[{0},{1}]",g.Key, g.Count));
var data = string.Format("[{0}]",string.Join(",", result));
I have an object with number of records in this format with Name in Text format -
Id Name
1 A,B,C
2 A,B
3 A
4 B
5 A,C
6 A,D
I have a multi-select picklist with values as:
A
B
C
D
So if I select B and C in my picklist, then I must have the records from the table in which name consists of B or C or (B and C) i.e. in this case, it should show 4 records:
1 A,B,C
2 A,B
4 B
5 A,C
I am using SQL query with IN and INCLUDES keyword but I am unable to achieve this functionality.
This depends on how many options you have for using soql. One way I can see it is:
Object__c myObject = new Object__c();
object.Name = 'A,B,D';
object.Multi_Select_Name__c = 'A;E';
insert object;
String query = 'select Id from Object__c';
//now we want to do a multi picklist, which is a String separated by ';'
List<String> nameSelections = object.Multi_Select_Name__c.split(';');
if(nameSelections.size() > 0) {
query += ' where ';
for(Integer i = 0; i < nameSelections.size(); i++) {
nameSelections[i] = 'Name like \'%' + nameSelections[i] + '%\'';
}
query += String.join(nameSelections, ' or ');
}
List<Object__c> objects = (List<Object__c)database.query(query);
This will create a query like:
'select Id from Object__c where Name like '%A%' or Name like '%E%'
Selecting all Object__c's where Name contains either A or E, generated from your multiselect.
Syntax errors may apply, I just wrote this real quick :) Ill double check myself.
EDIT:
This code works to build the query
String query = 'select Id from Object__c';
String multiSelect = 'A;E';
List<String> nameSelections = multiSelect.split(';');
if(nameSelections.size() > 0 ){
query += ' where ';
for(Integer i = 0; i < nameSelections.size(); i++) {
nameSelections[i] = 'Name like \'%' + nameSelections[i] + '%\'';
}
query += String.join(nameSelections, ' or ');
}
system.debug(LoggingLevel.INFO, query);
I am trying to write a dapper query for IN clause, but it's not working throwing casting error saying "Conversion failed when converting the nvarchar value 'A8B08B50-2930-42DC-9DAA-776AC7810A0A' to data type int." . In below query fleetAsset is Guid converted into string.
public IQueryable<MarketTransaction> GetMarketTransactions(int fleetId, int userId, int rowCount)
{
//Original EF queries which I am trying to convert to Dapper
//var fleetAsset = (from logicalFleetNode in _context.LogicalFleetNodes
// where logicalFleetNode.LogicalFleetId == fleetId
// select logicalFleetNode.AssetID).ToList();
////This query fetches guid of assetprofiles for which user having permissions based on the assets user looking onto fleet
//var assetProfileIds = (from ap in _context.AssetProfileJoinAccounts
// where fleetAsset.Contains(ap.AssetProfile.AssetID) && ap.AccountId == userId
// select ap.AssetProfileId).ToList();
var fleetAsset = _context.Database.Connection.Query<string>("SELECT CONVERT(varchar(36),AssetID) from LogicalFleetNodes Where LogicalFleetId=#Fleetid",
new { fleetId }).AsEnumerable();
//This query fetches guid of assetprofiles for which user having permissions based on the assets user looking onto fleet
var sql = String.Format("SELECT TOP(#RowCount) AssetProfileId FROM [AssetProfileJoinAccounts] AS APJA WHERE ( EXISTS (SELECT " +
"1 AS [C1] FROM [dbo].[LogicalFleetNodes] AS LFN " +
"INNER JOIN [dbo].[AssetProfile] AS AP ON [LFN].[AssetID] = [AP].[AssetID]" +
" WHERE ([APJA].[AssetProfileId] = [AP].[ID]) " +
" AND ([APJA].[AccountId] = #AccountId AND LogicalFleetId IN #FleetId)))");
var assetProfileIds = _context.Database.Connection.Query<Guid>(sql, new { AccountId = userId, FleetId = fleetAsset, RowCount=rowCount });
Dapper performs expansion, so if the data types match, you should just need to do:
LogicalFleetId IN #FleetId
(note no parentheses)
Passing in a FleetId (typically via an anonymous type like in the question) that is an obvious array or list or similar.
If it isn't working when you remove the parentheses, then there are two questions to ask:
what is the column type of LocalFleetId?
what is the declared type of the local variable fleetAsset (that you are passing in as FleetId)?
Update: test case showing it working fine:
public void GuidIn_SO_24177902()
{
// invent and populate
Guid a = Guid.NewGuid(), b = Guid.NewGuid(),
c = Guid.NewGuid(), d = Guid.NewGuid();
connection.Execute("create table #foo (i int, g uniqueidentifier)");
connection.Execute("insert #foo(i,g) values(#i,#g)",
new[] { new { i = 1, g = a }, new { i = 2, g = b },
new { i = 3, g = c },new { i = 4, g = d }});
// check that rows 2&3 yield guids b&c
var guids = connection.Query<Guid>("select g from #foo where i in (2,3)")
.ToArray();
guids.Length.Equals(2);
guids.Contains(a).Equals(false);
guids.Contains(b).Equals(true);
guids.Contains(c).Equals(true);
guids.Contains(d).Equals(false);
// in query on the guids
var rows = connection.Query(
"select * from #foo where g in #guids order by i", new { guids })
.Select(row => new { i = (int)row.i, g = (Guid)row.g }).ToArray();
rows.Length.Equals(2);
rows[0].i.Equals(2);
rows[0].g.Equals(b);
rows[1].i.Equals(3);
rows[1].g.Equals(c);
}