i have couple of tables that i am joining
organization table:
id, name
registration table:
id, name
after i execute this is how it looks
org_id org_name reg_name reg_id
--------------------------------------------------------
329 abc regname1 311
329 abc regname2 298
what i want to do is to display the data ONE row like this:
org_id org_name reg_name reg_id
------------------------------------------------------------------------
329 abc regname1;regname2 311;298
note: my reg_name is dynamic, it might be one or ten.
I'd say give this post a try: comma separated list in SQL-server.
What you're looking for is a SQLCLR custom aggregator. You would then use your hand crafted custom aggregator in much the same way you'd use the SUM aggregator.
A rough-cut custom aggregator that will accumulate strings, sort them and then join with a semi-colon would looke like this:
[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.UserDefined, MaxByteSize=-1)]
public struct JoinString : IBinarySerialize
{
List<String> strings;
public void Init()
{
strings = new List<String>();
}
public void Accumulate(SqlString input)
{
strings.Add(input.Value);
}
public void Merge(JoinString Group)
{
strings.AddRange(Group.strings.ToArray());
}
public SqlString Terminate()
{
strings.Sort();
return new SqlString(String.Join(";", strings.ToArray()));
}
public void Read(System.IO.BinaryReader r)
{
int items = r.ReadInt32();
strings = new List<String>(items);
for (var i = 0; i < items; i++)
strings.Add(r.ReadString());
}
public void Write(System.IO.BinaryWriter w)
{
w.Write(strings.Count);
foreach (var s in strings)
w.Write(s);
}
}
In Oracle, you can use the list_aggr function also.
For example:
SELECT deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employees
FROM emp
GROUP BY deptno;
Your query should like something:
SELECT a.org_id,a.org_nam, LISTAGG(reg_name, ';') WITHIN GROUP (ORDER BY a.org_id) AS reg_name, LISTAGG(reg_id, ';') WITHIN GROUP (ORDER BY a.org_id) AS reg_id
FROM organization a , registration b
where b.org_id = a.org_id
GROUP BY a.org_id,a.org_nam;
Related
I have an odd issue with AutoMapper (I'm using .NET core 3.1 and AutoMapper 10.1.1)
I'm doing a simple project to list and a simple projected count for total records:
var data = Db.Customers
.Skip((1 - 1) * 25)
.Take(25)
.ProjectTo<CustomerViewModel>(Mapper.ConfigurationProvider)
.ToList();
var count = Db.Customers
.ProjectTo<CustomerViewModel>(Mapper.ConfigurationProvider)
.Count();
The first line creates the expected SQL:
exec sp_executesql N'SELECT [c].[Code], [c].[Id], [c].[Name], [c].[Website], [s].Name
FROM [Customers] AS [c]
INNER JOIN [Status] AS [s] ON [s].id = [c].StatusId
ORDER BY (SELECT 1)
OFFSET #__p_0 ROWS FETCH NEXT #__p_1 ROWS ONLY',N'#__p_0 int,#__p_1 int',#__p_0=0,#__p_1=25
The second line, the Count(). Seems to ignore the projection entirely:
SELECT COUNT(*)
FROM [Customers] AS [c]
The result of this is that any customer with a null StatusId will be excluded from the first query but included in the count in the second. Which breaks paging.
I would have thought that project should create something like:
SELECT COUNT(*)
FROM [Customers] AS [c]
INNER JOIN [Status] AS [s] ON [s].id = [c].StatusId
Anyone know why the Count() is ignoring the ProjectTo<>?
Edit
Execution plan:
value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[Domain.Customer]).Select(dtoCustomer
=> new CustomerViewModel() { Code = dtoCustomer.Code, Id = dtoCustomer.Id, Name = dtoCustomer.Name, StatusName =
dtoCustomer.Status.Name, Website = dtoCustomer.Website})
Edit 2021/02/19
Mappings plan:
EF entities -
public class Customer
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public string Code { get; private set; }
public string Website { get; private set; }
public CustomerStatus Status { get; private set; }
public Customer() { }
}
public class CustomerStatus
{
public Guid Id { get; private set; }
public string Name { get; private set; }
}
ViewModel -
public class CustomerViewModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Website { get; set; }
public string StatusName { get; set; }
}
Mapping -
CreateMap<Customer, CustomerViewModel>();
Edit 2021/02/20 - Manually Excluding Status
As pointed out in #atiyar answer you can manually exclude the status. This crosses me as a work around. My reasoning is this:
If you execute this query, as the very root query:
Db.Customers.ProjectTo<CustomerViewModel>(_mapper.ConfigurationProvider)
You get:
exec sp_executesql N'SELECT TOP(#__p_0) [c].[Id], [c].[Name], [c0].[Name]
AS [StatusName]
FROM [Customers] AS [c]
INNER JOIN [CustomerStatus] AS [c0] ON [c].[StatusId] = [c0].[Id]',N'#__p_0
int',#__p_0=5
This shows automapper understands and can see that there is a needed relationship between Status and Customer. But when you apply the count mechanism:
Db.Customers.ProjectTo<CustomerViewModel>(_mapper.ConfigurationProvider).Count()
Suddenly, the understood relationship between Status and Customer is lost.
SELECT COUNT(*)
FROM [Customers] AS [c]
In my experience with Linq each query step modifies the previous step in a predicable way. I would have expected the count to build on the first command and include the count as part of that.
Interestingly, if you execute this:
_context.Customers.ProjectTo<CustomerViewModel>(_mapper.ConfigurationProvider).Take(int.MaxValue).Count()
Automapper applies the relationship and the result is what I would have expected:
exec sp_executesql N'SELECT COUNT(*)
FROM (
SELECT TOP(#__p_0) [c].[Id], [c].[Name], [c0].[Name] AS [Name0], [c0].[Id]
AS [Id0]
FROM [Customers] AS [c]
INNER JOIN [CustomerStatus] AS [c0] ON [c].[StatusId] = [c0].[Id]
) AS [t]',N'#__p_0 int',#__p_0=2147483647
Edit 2021/02/20 - Latest Version
Seems behaviour is the same in the latest version.
FYI: We have a scenario where records are imported on a regular basis from another application. We were hoping to use the inner join to exclude the records that don't have a matching record in another table. Then those records would be updated at a later point by the import process.
But from the application point of view it should always ignore those records hence the inner join and the status being mandatory. But we will have to manually exclude them (as per atiyar's solution) using the where to prevent paging from returning blown out page count numbers.
Edit 2021/02/20 - Further Digging
This does appear to be a design choice by the EF team and an optimisation. The assumption here is that if the relationship is non-null able. Then the join wont be included as a performance boost. The way around this is as suggested by #atiyar. Thanks for the help everyone #atiyar & #Lucian-Bargaoanu.
I have tested your code in .NET Core 3.1 with Entity Framework Core 3.1 and AutoMapper 10.1.1. And -
your first query generates a LEFT JOIN, not an INNER JOIN like you posted. So, the result from that query will not exclude any customer with a null StatusId. And, the generated SQL is same with ProjectTo<> and manual EF projection. I'd suggest to check your query and generated SQL again to make sure.
your second query generates the same SQL, the SQL you have posted, with ProjectTo<> and manual EF projection.
A solution for you :
If I understand correctly, you are trying to get -
a list of Customer, within the specified range, who has a related Status
the count of all such customers in your database.
Try the following -
Add a nullable foreign-key property in your Customer model -
public Guid? StatusId { get; set; }
This will help to simplify your queries and the SQL they generate.
To get your expected list, modify the first query as -
var viewModels = Db.Customers
.Skip((1 - 1) * 25)
.Take(25)
.Where(p => p.StatusId != null)
.ProjectTo<CustomerViewModel>(_Mapper.ConfigurationProvider)
.ToList();
It will generate the following SQL -
exec sp_executesql N'SELECT [t].[Code], [t].[Id], [t].[Name], [s].[Name] AS [StatusName], [t].[Website]
FROM (
SELECT [c].[Id], [c].[Code], [c].[Name], [c].[StatusId], [c].[Website]
FROM [Customers] AS [c]
ORDER BY (SELECT 1)
OFFSET #__p_0 ROWS FETCH NEXT #__p_1 ROWS ONLY
) AS [t]
LEFT JOIN [Statuses] AS [s] ON [t].[StatusId] = [s].[Id]
WHERE [t].[StatusId] IS NOT NULL',N'#__p_0 int,#__p_1 int',#__p_0=0,#__p_1=25
To get your expected count, modify the second query as -
var count = Db.Customers
.Where(p => p.StatusId != null)
.Count();
It will generate the following SQL -
SELECT COUNT(*)
FROM [Customers] AS [c]
WHERE [c].[StatusId] IS NOT NULL
I want to create lookup relationship between two custom object project and todo object but not able to do so
This is the code to create lookup relationship between two object. It contain class two methods for create project and the create task respectively
public class ProjectAssignment { *//class ProjectAssignment*
public static void createProject(){ *//method to create an object*
Project__c pr=new Project__c();
pr.Name='IseeIt'; //name of project
pr.Start_Date__c= date.today()`
pr.Description__c='This Is To Do Kind Project';
pr.End_Date__c=date.today()+5;
insert pr;
}
public static void createTask(){ *//create todo method
//Code to fetch data*
Project__c pr = [Select Id from Project__c]; *//query for lookup relationship between project and todo*
ToDo__c td = new ToDo__c();
td.Project__c= pr.Id; *//fetchhing project id*
td.Start_Date__c=date.today();
td.Description__c='This is the first task';
td.End_Date__c=date.today()+2;
insert td;
}
}
"System.QueryException: List has more than 1 row for assignment to SObject"
because their are two project i have created in the project object
Try this.
Project__c pr = [Select Id from Project__c];
For this, you have to Select the query in where clause and put LIMIt 1 so that they give you only one record.
Project__c pr = [Select Id from Project__c where name = 'IseeIt' LIMIT 1];
Please Help!!!
I have this Apex controller class that I'm trying to deploy from Salesforce Sandbox to Production but I'm not getting the code coverage required!!
the class is consuming 3 objects, one is stranded and 2 are custom and constructing a hierarchy tree view for those three objects.
//Apex Class
public class TeeView {
/* Wrapper class to contain the nodes and their children */
public class cNodes
{
Public StandardObject gparent {get;set;}
public List<CustomObject1__c> parent {get; set;}
Public List<CustomObject2__c> child {get;set;}
public cNodes(StandardObject gp, List<CustomObject1__c> p, List<CustomObject2__c> c)
{
parent = p;
gparent = gp;
child = c;
}
}
/* end of Wrapper class */
Public List<cNodes> hierarchy;
Public List<cNodes> getmainnodes()
{
hierarchy = new List<cNodes>();
List<StandardObject> tempparent = [Select Id,Name , End_Date__c, Owner.Name Account.Name from Contract ];
for (Integer i =0; i< tempparent.size() ; i++)
{
List<CustomObject1__c> tempchildren = [Select Id,Name, Owner.Name , (select Id,Name, Owner.Name from CustomObject2__r) from CustomObject1__c where Related_Field__c = :tempparent[i].Id];
List<CustomObject2__c> tempchild = [Select Id,Name Owner.Name from CustomObject2__c where Related_Field__c= :tempparent[i].Id];
hierarchy.add(new cNodes(tempparent[i],tempchildren, tempchild));
}
return hierarchy;
}
}
//Test Class
#isTest
public class treeviewTest
{
static testMethod void test1()
{
test.startTest();
Account acc = new Account(Name = 'Unit test account');
insert acc;
StandardObject c = new StandardObject(
Name = 'test',
AccountId = acc.Id,
Status = 'Draft',
StartDate = System.today());
try
{
insert c;
}
catch (Exception ex)
{
ApexPages.addMessages(ex);
}
List<StandardObject> standard = [select Id, Name from StandardObject where Name = 'test'];
system.assertequals(standard.size(), 1);
CustomObject1__c s = new CustomObject1__c(
Related_StandardObjectField__c = c.Id,
Name = 'test'
);
try
{
insert s;
}
catch (Exception ex)
{
ApexPages.addMessages(ex);
}
List<CustomObject1__c> cus1 = [select Id, Name from CustomObject1__c where Name = 'test'];
system.assertequals(cus1.size(), 1);
insert new CustomObject2__c(Related_StandardObjectField__c = c.Id, Description__c = 'test');
List<CustomObject2__c> cus2 = [select Id, Name from CustomObject2__c where Description__c = 'test'];
system.assertequals(cus2.size(), 1);
insert new CustomObject2__c(Related_CustomObject1Field__c = s.Id, Description__c = 'test');
List<Mods__c> cus3 = [select Id, Name from Mods__c where Description__c = 'test'];
system.assertequals(cus3.size(), 1);
treeView view = new treeView();
view.getmainnodes();
test.stopTest();
}
}
Oh man, where do we even start... Looks like you copy-pasted some code at random trying to get the test to work? Your code works on Contract, CustomObject1__c, CustomObject2__c. Your unit test inserts Contract, Subaward__c, Mods__c. Was this some attempt at anonymizing your code? And what's this List<StandardObject> thing, it shouldn't even compile.
I think you need help of a developer with more experience, you've cobbled something together but it doesn't follow the Salesforce best practices for code...
About the main class
Smart use of subqueries will mean
you waste less SOQL (it's bad idea to blindly select all records without any filters but whatever),
don't run into errors related to "too many SOQL statements" (the limit is 100 so since you query in a loop - your code will crash and burn the moment you have around 50 Contracts)
lead to compact code occupying less lines (easier to maintain & cover with tests).
I'd go with something like that, query Contract & first related list in one go and then the parents + children in another query. Using a Map to link them for displaying.
List<Contract> levelOneAndTwo = [SELECT Id, Name, End_Date__c, Owner.Name, Account.Name,
(SELECT Id,Name, Owner.Name FROM Subawards__r)
FROM Contract];
Map<Id, SubAward__c> levelTwoAndThree = new Map<Id, Subaward__c>([SELECT Id,
(SELECT Id,Name Owner.Name FROM Mods__r)
FROM Subaward__c WHERE Contract__c = :levelOneAndTwo];
);
This wastes only 2 queries but fetches all contracts and their related data. Then you can loop over results building your wrapper object or just feed it directly to Visualforce for displaying.
About the unit test
You don't get the coverage because you're spread too thin. One unit test methods inserts only Contracts (so your main query will hit something, give you some coverage). Then another method (in completely different context) inserts some child objects - but they don't matter. In that other method there are 0 contracts so it won't even enter your main loop. Combine it into one?
Account acc = new Account(Name = 'Unit test account');
insert acc;
Contract c = new Contract(
Name = 'test contract',
AccountId = acc.Id,
Status = 'Draft',
StartDate = System.today()
);
insert c;
Subaward__c s = new Subaward(
Contract__c = c.Id,
Name = 'test'
);
insert s;
insert new Mod__c(Subaward__c = c.Id, Description__c = 'test');
Now you have nice tree hierarchy set up, from Account all the way down. Queries in the test should hit some data.
I was finally able to get %100 code coverage for the above class by adding these lines to the test class:
treeView view = new treeView();
view.getmainnodes();
system.assert(view.hierarchy.size() > 0);
If I had a User model that looks like this:
public class User
{
public Guid Id { get; set; }
public DateTime CreatedAtUtc { get; set; }
public string Username { get; set; }
public string Country { get; set; }
}
...and I'd perform a query which starts at a given row and fetches a limited amount of further rows (basically for paginating over the results) that looks like this:
var spaniards = connection.Query<User>(
"select * from Users where Country=#Country OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY",
new { Country = "Spain" }).ToList();
.. using Dapper(.Net), would it be possible to get that particular, limited result set AND the total count of rows in one single query and if so.. how?
One way to do solve this is using the splitOn functionality, together with count(1) over()
Let's assume the following sql string:
var sql = "select *, overall_count = COUNT(1) OVER() from Users ORDER BY Id Asc OFFSET 5 ROWS;"
together with the following dapper.net call:
HashSet<int> hashSet = new HashSet<int>();
Func<User, int, User> map = (result, count) =>
{
hashSet.Add(count);
return result;
};
await connection.QueryAsync(sql, map, "overall_count").ConfigureAwait(false);
This will split the result of the dapper call into two parts (just like with joining different tables) and it will call the map functor each time, in this case we simply store the count in a hashset (you could then check if the hashSet.Count is 1)
Hope this helps
PS: I'm not certain that your OFFSET works without any ORDER BY
I am using a SOQL like below
select COUNT(Transaction_Type_Id__c) tt, Id__c from Analysis_set_header__c group by Id__c
The Object having total 42 records.but I am getting error like
Aggregate query does not support queryMore(), use LIMIT to restrict the results to a single batch.
Here is my batch class
global class AnalysisSetWsCodeBatchClass implements Database.Batchable<sObject>, Database.AllowsCallouts {
public String query = 'select COUNT(Transaction_Type_Id__c) tt, Id__c from Analysis_set_header__c group by Id__c';
global Database.QueryLocator start(Database.BatchableContext BC) {
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, AggregateResult[] groupedResults) {
set<String> setAh = new set<String>();
for (AggregateResult ar : groupedResults) {
System.debug('--------header---' + ar.get('Id__c'));
setAh.add((string) ar.get('Id__c'));
}
system.debug('----------------setAh----------------------'+setAh);
if(!setAh.isEmpty()){
AnalysisSetCodeWsUpdate aw = new AnalysisSetCodeWsUpdate();
aw.updateAnalysisSetCode(setAh);
}
}
global void finish(Database.BatchableContext BC){
//you can also do some after-the-job-finishes work here
}
}
how many records do you have when you query SELECT ID FROM Analysis_set_header__c? More than 43? My suggestion is to change your SOQL string to the following: SELECT COUNT_DISTINCT(Transaction_Type_Id__c) tt, Id__c from Analysis_set_header__c order by COUNT_DISTINCT(Id__c)